I have been having some inexplicable SFINAE problems in a program I'm writing, so I boiled it down to a freestanding example program:
#include <type_traits>
struct Base { };
struct Derived : public Base { };
template<typename T>
struct From { };
template<typename T>
struct To {
template<typename U>
To(const From<U>& other) {
static_assert(std::is_convertible<U*, T*>::value, "error");
}
};
int main() {
From<Derived> a;
To<Base> b = a;
}
This program compiles without error or warning. However, this:
#include <type_traits>
struct Base { };
struct Derived : public Base { };
template<typename T>
struct From { };
template<typename T>
struct To {
template<typename U>
To(const From<typename std::enable_if<true, U>::type>& other) {
// this ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
static_assert(std::is_convertible<U*, T*>::value, "error");
}
};
int main() {
From<Derived> a;
To<Base> b = a;
}
Gives the following error:
test.cpp: In function int main():
test.cpp:22:18: error: conversion from From<Base> to non-scalar type To<Derived> requested
Which is because the substitution fails I presume, and the constructor isn't seen.
Am I doing SFINAE wrong, or is this a compiler bug? I am using rubenvb's GCC 4.7.1 on Windows (with std=c++11 if it makes a difference).
I'd use a default template argument, that way the argument can be deduced:
#include <type_traits>
struct Base { };
struct Derived : public Base { };
template<typename T>
struct From { };
template<typename T>
struct To {
template<typename U, class = typename std::enable_if<true, U>::type>
To(const From<U>& other) {
// this ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
static_assert(std::is_convertible<U*, T*>::value, "error");
}
};
int main() {
From<Base> a;
To<Derived> b = a;
}
Note that that causes the static_assert to fail since you are using std::is_convertible the other way around. It should be:
static_assert(std::is_convertible<T*, U*>::value, "error");
In your example, the template type U can't be deduced. In my code, it can be deduced, since it is being used as a template argument for the other argument in the constructor. In your code, the compiler sees a std::enable_if<true, U>::type and can't deduce what that U type is. The fact that the result of that enable_if is being used as a template argument for From doesn't help at all, since U needs to be deduced before that.
Related
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
Inside a specialization for a templated class I call a function which should not compile, but in my mind it shouldn't matter as I pass false as a template argument and the compiler shouldn't instantiate that class in the first place:
#include <iostream>
template <typename T>
struct outerstruct
{
template <bool b>
struct innerStruct
{
};
template<>
struct innerStruct<true>
{
using type = std::underlying_type_t<T>;
};
template<>
struct innerStruct<false>
{
};
innerStruct<false> member; // std::underlying_type_t cannot be called on a T,
// even though that class shouldn't be instantiated
// because I'm passing false (or I think)
};
int main()
{
outerstruct<int> a;
}
Why is innerStruct<true> being instantiated and the compiler trying to call underlying_type if I pass the false argument?
Very confusingly for me, I can make it behave the way I want (ie., have the compiler ignore the call to std::underlying_type_t) by just adding another template argument to innerClass, here I add a U:
#include <iostream>
template <typename T>
struct outerstruct
{
template <typename ExtraArg, bool b>
struct innerStruct
{
};
template<typename ExtraArg>
struct innerStruct<ExtraArg, true>
{
using type = std::underlying_type_t<T>;
};
template<typename ExtraArg>
struct innerStruct<ExtraArg, false>
{
};
//innerStruct<bValue> member;
innerStruct<int, true> member; // Now it's working as I expected,
// I can pass int or whatever argument to ExtraArg
// and it compiles as long as I have 'false'
};
int main()
{
outerstruct<int> a;
}
What is the difference between the first and the second examples? What business does template specialization innerStruct<true> have being instantiated if in the first example I only called innerStruct<false>
I'm setting up a variadic template function to be able to call various function overloads on a specific series of classes. So far, I've been able to "break" the compilation when an unsupported class is passed to the function, but I'd like to be able to provide a valid fallback to handle the "unsupported" scenario at runtime.
The current implementation goes like this :
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, typename = std::enable_if_t<is_my_class_v<T>>>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, typename... Ts>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b); // works
run(c); // does not compile
}
Here, the two most promising (but still failing) attempts I've made to have a fallback for run:
1 - Adding a simple ! in front of is_my_class<T>, giving me the following error : error C2995: 'void run(T)': function template has already been defined
template<typename T, typename = std::enable_if_t<!is_my_class_v<T>>>
void run(T myClass)
{
printf("Not supported\n");
};
2 - Making a more "primary" template definition, which yields a sad but obvious : error C2668: 'run': ambiguous call to overloaded function
template<typename T>
void run(T myClass)
{
printf("Not supported\n");
};
EDIT
I forgot to specify I was looking for a solution also compatible with C++11/14
You could avoid enable_if entirely, and just use a compile-time if to to decide what code to execute:
template<typename T>
void run(T myClass)
{
if constexpr (is_my_class_v<T>)
runOverload(myClass);
else
printf("Not supported\n");
}
Here's a demo.
Even though I'd recommend #cigien 's solution if c++17 is avaiable, I would like to add, that the ambiguity problem can be mitigated pre c++17 by changing the type of the enable_if template argument, and hence changing the signature of the "fallback function". The following code should work fine:
template<typename T, std::enable_if_t<!is_my_class_v<T>, int> = 0>
//template<typename T>
void run(T myClass) {
printf("error\n");
};
Full code available on CompilerExplorer, tested on GCC and Clang trunk.
I'd also like to add, that in all use cases I can imagine, it is better to have a compile time error (which you could for instance achieve by using a static assertion instead of SFINAE).
Here you can read about why the function delcaration is not ambiguous, even when using two "ints" as template arguments.
I wrote following code and it works. I think you just messed up with syntax of SFINAE.
#include <iostream>
#include <type_traits>
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, std::enable_if_t<is_my_class_v<T>,int> = 0>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, std::enable_if_t<not is_my_class_v<T>,int> = 0>
void run(T myClass)
{
printf("Not supported\n");
};
// wrote an extra SFINEA here to ensure that Ts aren't empty - else it might be an ODR issue despite the fact that it compiles
template<typename T, typename... Ts, std::enable_if_t<sizeof...(Ts) != 0,int> = 0>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b);
run(c);
}
It prints
ClassA overload
ClassB overload
Not supported
Have a fallback runOverload template
template<typename T>
void runOverload(T myClass)
{
printf("Not supported\n");
};
How i can use "using" keyword in multiple inheritance when parameter pack is template parameter of base class?
Code below compiles just fine
struct A
{
void foo(int) {}
};
struct B
{
void foo(int) {}
};
template<typename ...Types>
struct C : public Types...
{
using Types::foo...;
};
int main()
{
C<A,B> c;
}
But if i use template instead of A and B - I've got compile error
template<typename T>
struct TA {};
template<>
struct TA<int>
{
void foo(int) {}
};
template<>
struct TA<double>
{
void foo(int) {}
};
template<typename ...Types>
struct TC : public TA<Types>...
{
using TA<Types>::foo...; // ERROR C3520
};
Error:
error C3520: 'Types': parameter pack must be expanded in this context
How to rewrite second piece of code to get it work?
PS
I tried this code with gcc and it's compiles without errors. But now I am using msvc...
Since it's a known MSVC bug, if you still want to make this work with MSVC you'll have to do it the (not very) hard way:
template <typename ...Types>
struct TC;
template <typename T>
struct TC<T> : TA<T>
{
using TA<T>::foo;
};
template <typename T, typename ...Types>
struct TC<T, Types...> : public TC<T>, public TC<Types...>
{
using TC<T>::foo;
using TC<Types...>::foo;
};
In my example I have a class Foo<T>. In my function test I need to get the template parameter of Foo otherwise the normal type. First I started to use std::conditional but forgot that the template parameters must all be valid, no matter which one is picked. Is the only way to create a type-specialisation for non-Foo types?
Example
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const T& a)
{
// actually I would have used !is_foo<T>::value for the first arg
// but this check is fine to minimise the example
using MY_TYPE = typename std::conditional<
std::is_same<T, int>::value,
T,
typename T::M>::type; // <---Error: error: type 'int' cannot be used prior to '::' because it has no members
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
Well you could make an UnFoo helper to get the right type for you:
template <typename T>
struct UnFoo {
using type = T;
};
template <typename T>
struct UnFoo<Foo<T>> {
using type = T;
};
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename UnFoo<T>::type; //maybe with a helper to get rid of typename
}
Another option would be to write an overload for Foo<T> and have it delegate to the other function, but that depends on what your real test function does.
You can do some void_t magic to allow SFINAE to figure help you out:
#include <type_traits>
#include <iostream>
#include <typeinfo>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
// primary template handles types that have no nested ::T member:
template< class T, class = void_t<> >
struct M_or_T { using type = T; };
// specialization recognizes types that do have a nested ::T member:
template< class T >
struct M_or_T<T, void_t<typename T::M>> { using type = typename T::M; };
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename M_or_T<T>::type;
std::cout << typeid(MY_TYPE).name() << "\n";
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
What happens is that the second overload of M_or_T substitution fails for int (and for any type without a type member M) and thus the first overload is chosen. For types which have a type member M, a more specialized second overload is chosen.
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const Foo<T>& a)
{
using MY_TYPE = Foo<T>::M;
testOther<MY_TYPE>(a);
}
template <typename T>
void test(const T& a)
{
using MY_TYPE = T;
testOther<MY_TYPE>(a);
}
template <typename T, typename S>
void testOther(const S& a)
{
// do stuff
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
I'm not exactly sure what you wanted, but I hope this is what you wanted. It might be a bit off. I didn't compile this.