C++ template inheritance with pointer type - c++

I have a template struct and I want that every pointer type instantiates the const version, to avoid having duplicate templates. This doesn't work, but it work with the char pointer. How can I make it work with any pointer?
#include <iostream>
template<class T>
struct Foo {
static void bar() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
// Doesn't work
// template<class T>
// struct Foo<T*> : Foo<const T*> {};
// Works
template<>
struct Foo<char*> : Foo<const char*> {};
int main() {
Foo<char*>::bar();
}

Since T can also be T const, you are creating infinite inheritance, hence the incomplete type error.
Since const T* matches T* with T being char const for example, you makes the class inheriting from itself be inheriting from Foo<T const*>, which expands to Foo<char const const*> which collapses to Foo<char const*>.
Simply add a constraint and your code works as expected:
// Now works
template<class T> requires (not std::is_const_v<T>)
struct Foo<T*> : Foo<const T*> {};
Live example

You can do it if you extract a base class from Foo:
#include <iostream>
template<class T>
struct FooImpl {
static void bar() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<class T>
struct Foo : FooImpl<T> {};
template<class T>
struct Foo<T*> : FooImpl<const T*> {};
int main() {
Foo<char*>::bar();
Foo<const char*>::bar();
}
Unlike #Guillaume answer, this does not require C++20.

Related

Code compiles on one machine, but not on another [duplicate]

Is it legal to use an incomplete type in a template if the type is complete when the template is instantiated?
As below
#include <iostream>
struct bar;
template <typename T>
struct foo {
foo(bar* b) : b(b) {
}
void frobnicate() {
b->frobnicate();
}
T val_;
bar* b;
};
struct bar {
void frobnicate() {
std::cout << "foo\n";
}
};
int main() {
bar b;
foo<int> f(&b);
f.frobnicate();
return 0;
}
Visual Studio compiles the above without complaining. GCC issues the warning invalid use of incomplete type 'struct bar' but compiles. Clang errors out with member access into incomplete type 'bar'.
The code is ill-formed, no diagnostic required.
[temp.res.general]/6.4
The validity of a template may be checked prior to any instantiation.
The program is ill-formed, no diagnostic required, if:
...
— a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, ...
If you absolutely can't define bar before the template, there is a workaround: you can introduce an artifical dependency on the template parameter.
template <typename T, typename, typename...>
struct dependent_type {using type = T;};
template <typename T, typename P0, typename ...P>
using dependent_type_t = typename dependent_type<T, P0, P...>::type;
Then use dependent_type_t<bar, T> instead of bar.
Clang is correct in reporting an error (as opposed to a warning or being silent about it), though MSVC's and GCC's behavior are also consistent with the standard. See #HolyBlackCat's answer for details on that.
The code you posted is ill-formed NDR. However, what you want to do is feasible.
You can defer the definition of template member functions the same way you would for a non-template class. Much like non-template classes, as long as these definitions requiring bar to be a complete type happen only once bar is complete, everything is fine.
The only hiccup is that you need to explicitly mark the method as inline to avoid ODR violations in multi-TU programs, since the definition will almost certainly be in a header.
#include <iostream>
struct bar;
template <typename T>
struct foo {
foo(bar* b) : b(b) {
}
inline void frobnicate();
T val_;
bar* b;
};
struct bar {
void frobnicate() {
std::cout << "foo\n";
}
};
template <typename T>
void foo<T>::frobnicate() {
b->frobnicate();
}
int main() {
bar b;
foo<int> f(&b);
f.frobnicate();
return 0;
}
If you want to customise a template using a forward declaration as a temlpate argument, you can do this (wihtout warnings or errors):
template <typename T, typename = T>
class print_name;
so when you do a partial specialization, you use the second, unspecialized template parameter for your calls:
struct john;
template <typename T>
class print_name<john, T>
{
public:
void operator()(const T& f) const
{
std::cout << f.name << std::endl;
}
};
In this context T is not incomplete. But when you instantiate print_name<john>, SFINAE will kick.
Here is a full example:
#include <iostream>
template <typename T, typename = T>
class print_name;
struct john;
template <typename T>
class print_name<john, T>
{
public:
void operator()(const T& f) const
{
std::cout << f.name << std::endl;
}
};
struct slim;
template <typename T>
class print_name<slim, T>
{
public:
void operator()(const T& f) const
{
std::cout << f.myName << std::endl;
}
};
#include <string>
struct john
{
std::string name;
};
struct slim
{
std::string myName;
};
int main()
{
print_name<john>{}(john{"John Cena"});
print_name<slim>{}(slim{"Slim Shady"});
return 0;
}
https://godbolt.org/z/czcGo5aaG

Check if a class has a possibly-overloaded function call operator

I am wondering whether it would be possible to implement a trait in C++20 to check if a type T is such that it has a possibly overloaded/possibly templated function call operator: operator().
// Declaration
template <class T>
struct has_function_call_operator;
// Definition
???
// Variable template
template <class T>
inline constexpr bool has_function_call_operator_v
= has_function_call_operator<T>::value;
so that a code such as the following would lead to the correct result:
#include <iostream>
#include <type_traits>
struct no_function_call_operator {
};
struct one_function_call_operator {
constexpr void operator()(int) noexcept;
};
struct overloaded_function_call_operator {
constexpr void operator()(int) noexcept;
constexpr void operator()(double) noexcept;
constexpr void operator()(int, double) noexcept;
};
struct templated_function_call_operator {
template <class... Args>
constexpr void operator()(Args&&...) noexcept;
};
struct mixed_function_call_operator
: overloaded_function_call_operator
, templated_function_call_operator {
};
template <class T>
struct has_function_call_operator: std::false_type {};
template <class T>
requires std::is_member_function_pointer_v<decltype(&T::operator())>
struct has_function_call_operator<T>: std::true_type {};
template <class T>
inline constexpr bool has_function_call_operator_v
= has_function_call_operator<T>::value;
int main(int argc, char* argv[]) {
std::cout << has_function_call_operator_v<no_function_call_operator>;
std::cout << has_function_call_operator_v<one_function_call_operator>;
std::cout << has_function_call_operator_v<overloaded_function_call_operator>;
std::cout << has_function_call_operator_v<templated_function_call_operator>;
std::cout << has_function_call_operator_v<mixed_function_call_operator>;
std::cout << std::endl;
}
Currently it prints 01000 instead of 01111. If it is not doable in the broadest possible sense, it can be assumed that T is inheritable if that helps. The weirdest possible template metaprogramming tricks are welcome as long as they are fully compliant with the C++20 standard.
&T::operator() is ambiguous for the 3 failing cases.
So your traits found is there is an unambiguous operator()
As you allow T to be not final, we might apply your traits to (fake) class with existing inherited operator() and class to test:
template <class T>
struct has_one_function_call_operator: std::false_type {};
template <class T>
requires std::is_member_function_pointer_v<decltype(&T::operator())>
struct has_one_function_call_operator<T>: std::true_type {};
struct WithOp
{
void operator()() const;
};
template <typename T>
struct Mixin : T, WithOp {};
// if T has no `operator()`, Mixin<T> has unambiguous `operator()` coming from `WithOp`
// else Mixin<T> has ambiguous `operator()`
template <class T>
using has_function_call_operator =
std::bool_constant<!has_one_function_call_operator<Mixin<T>>::value>;
template <class T>
inline constexpr bool has_function_call_operator_v
= has_function_call_operator<T>::value;
Demo

Trying to put partial in different header

I have the following program working fine. But once I get rid of the forward declaration of the primary specialization in bar.h. I got compilation error saying the specialization is not a class template. Why?
bar.h:
template<typename T>
struct Bar{
void bar()
{
std::cout<< "bar" << std::endl;
};
};
//template<typename T> // Looks like I need to forward declare this in order to have it compile and work, why?
//struct IsBar;
template<typename T>
struct IsBar<Bar<T>> {
static const bool value = true;
};
main.cpp:
#include "bar.h"
struct Raw{
};
template<typename T>
struct IsBar{
static const bool value = false;
};
template<typename Obj, bool>
struct function{
static void callbar(Obj obj){
obj.bar();
};
};
template<typename Obj>
struct function<Obj, false>{
static void callbar(Obj obj){
std::cout<< "no bar()" << std::endl;
};
};
int main()
{
typedef Bar<Raw> Obj;
Obj obj;
function<Obj, IsBar<Obj>::value> f;
f.callbar(obj);
return 0;
}
This is because
template<typename T>
struct IsBar<Bar<T>> {
static const bool value = true;
};
is a template specialization of the template IsBar<U> for U=Bar<T>. In order to specialize a template, you must first declare the general un-specialized template.
Moreover, for this to work properly (for example in SFINAE), you want the general version to contain the value=false:
template<typename T>
struct IsBar
: std::integral_constant<bool,false>
{};
template<typename T>
struct IsBar<Bar<T>>
: std::integral_constant<bool,true>
{};
As this functionality is closely related to struct Bar<>, it should be defined within the same header file as struct Bar<>, i.e. bar.h in your case.

Is there an elegant solution to distinguish between void and non void arguments in a template member function call?

Having a base class and its specialization for void:
#include <iostream>
template <typename T>
struct Base
{
typedef T result_type;
result_type get_value() { return result_type(); }
void process_value(result_type&&) {
std::cout << "type\n";
}
};
template <>
struct Base<void>
{
typedef void result_type;
void get_value() {};
void process_value(/*void&&*/) {
std::cout << "void\n";
}
};
template <typename T>
struct Derived : Base<T>
{
typedef typename Base<T>::result_type result_type;
// Returning void from a function call is fine.
result_type get() { return this->get_value(); }
void invoke() {
// If T is void: error: invalid use of void expression
this->process_value(get());
}
};
int main() {
Derived<int>().invoke();
// Trigger a compilation faluure:
Derived<void>().invoke();
}
Is there an elegant solution to distinguish between void and non void arguments in the call of 'process_value' (C++11 is fine) ?
One way is to substitute some empty class for void when it comes to passing the result as an argument. I don't find it very elegant, but it works:
#include <iostream>
template <typename T>
struct Base
{
typedef T result_type;
result_type eval() { return result_type(); }
result_type get_value() { return result_type(); }
void process_value(result_type&&) {
std::cout << "type\n";
}
};
template <>
struct Base<void>
{
struct value_type { };
typedef void result_type;
value_type eval() { return value_type{}; }
void get_value() {};
void process_value(value_type) {
std::cout << "void\n";
}
};
template <typename T>
struct Derived : Base<T>
{
typedef typename Base<T>::result_type result_type;
result_type get() { return this->get_value(); }
void invoke() {
this->process_value(this->eval());
}
};
int main() {
Derived<int>().invoke();
Derived<void>().invoke();
}
To find something more elegant, I feel you may need to refactor your original problem.
Slightly refactored and using C++11, this is another way to approach the problem:
First, your template specialization and main() are both unchanged:
template <>
struct Base<void>
{
typedef void result_type;
void get_value() {}
void process_value( void ) {
std::cout << "void\n";
}
};
int main() {
Derived<int>().invoke();
// Trigger a compilation faluure:
Derived<void>().invoke();
}
The first significant change is in the Derived template.
template <typename T>
struct Derived : Base<T>
{
typedef typename Base<T>::result_type result_type;
void invoke() {
this->process_value();
}
};
As you can see, I've removed get() so that may or may not any longer match your intent. It seemed that what you were attempting to do was to create a temporary something of type T and then interrogate it. That part is unchanged, but the semantics of it are moved to the Base class which uses SFINAE to do the right thing.
I've created a convenience function is_not_void() and a templated Enable_if (after Stroustrup) to make the code a little easier to read.
#include <iostream>
#include <type_traits>
template <typename T>
constexpr bool is_not_void() {
return !std::is_void<T>::value;
}
template <bool C, typename T>
using Enable_if = typename std::enable_if<C, T>::type;
Lastly the revised Base code:
template <typename T>
struct Base
{
typedef T result_type;
template <typename U, typename = Enable_if<is_not_void<U>(), U>>
U get_value() { return U(); }
template <typename U, typename = Enable_if<is_not_void<U>(), U>>
void do_process_value( U&&) {
std::cout << "type\n";
}
void process_value() {
do_process_value(get_value<T>());
}
};
As you can see, the templates have a default second template argument which can only be instantiated if the type is not void. The effect is that they will always be matched for everything other than void types but the void specialization will be used in the case that the type is void. As you already know, even the declaration of a void && argument will cause the compilation to fail, which is why we can't use Enable_if within the argument list of process_value.
When run, this code produces:
type
void

Is it possible to check at compile time whether a type is derived from some instantiation of a template?

I would like to write a template function which behaves one way if the passed type is derived from any template instantiation of another class, and another way if not.
I think the code below captures what I would like to do. Unfortunately Caller prints "generic" for both double and Derived.
#include <iostream>
template <typename T>
struct Base
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
void Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
(Note that Caller doesn't know which instantiation of Base that its parameter might derive from.)
It's calling the const T& overload because its a better match than const base<T>&. The reason is because calling the first requires no conversions and the second requires a derived-to-base conversion.
Here's a quick hack that shows you how it can be done (note the introduced base class):
#include <iostream>
#include <type_traits>
struct EvenMoreBase {};
template <typename T>
struct Base : EvenMoreBase
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
typename std::enable_if<!std::is_base_of<EvenMoreBase, T>::value>::type
Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
If you're able to use C++11 (or <type_traits> in general), the following is also a possible solution and covers not only types T : Base<T>, i.e. instances of the CRTP, but also T : Base<U> without another base class, as requested in your example.
#include <iostream>
#include <type_traits>
template <typename T>
struct Base
{
typedef T base_value_type;
};
struct Derived : public Base<int>
{
};
template <typename T, typename = T>
struct IsDerived
{
static const bool value = false;
};
template <typename T>
struct IsDerived<T, typename std::enable_if<std::is_base_of<Base<typename T::base_value_type>, T>::value, T>::type>
{
static const bool value = true;
};
template <typename T>
void Caller(const T&)
{
std::cout << IsDerived<T>::value << std::endl;
}
int main()
{
Caller(double()); // false
Caller(Derived()); // true
return 0;
}
Note the typedef T base_value_type - which might be called whatever your like. The idea is that each type T derived from Base<U> can leverage the knowledge of the base's template parameter. It doesn't matter if T == U or not. Trying to substitute the second parameter will fail as soon as you pass in a T that has no typedef T base_value_type and thus no specialization for this particular T will be generated.
EDIT: After processing your comment, and inspired by the thread I posted, I tried to somehow extract some base parameter U when examining some time type T : Base<U>. I don't think this can be done in the way you want, i.e. you pass whatever T and you extract U. However, you can do two things.
Simple Solution: If you have control over how derived classes are implemented, instead of adding a typedef in the base class, simply add a corresponding typedef in the derived class:
template <typename BaseParamType>
class Derived : public Base<BaseParamType>
{
public:
typedef BaseParamType base_param_type;
}
or, if you don't want derived classes to be class templates as well, simply hard code the type right into the type (you already know the type of the base parameter):
class Derived : public Base<int>
{
public:
typedef int base_param_type;
}
More involved solution: What you can do, at least for an expected subset of possible Us, is the following:
template <typename DerivedType,
typename BaseParamType = DerivedType,
bool = std::is_base_of<Base<BaseParamType>, DerivedType>::value>
struct Extract
{
typedef BaseParamType type;
};
template <typename T, typename U>
struct Extract<T, U, false>;
int main()
{
Extract<DerivedCRTP>::type; // CRTP - trivial
Extract<Derived, int>::type; // type == int, Derived is derived from Base<int>
Extract<Derived, double>::type; // compile-time error, undefined template
return 0;
}
This isn't as convenient as passing some instance of a type to a deducing template function and have it magically , but you can at least test if some type T derives from Base<U> and get a compile-time error if it doesn't.
Since the base class has to be a concrete class (not a template), it is not possible to know whether it is a template or a non-template class.
In another words :
struct A1 : public B1
{};
struct A2 : public B2<int>
{};
in both of these cases both base classes are concrete types.