This question already has answers here:
Selecting a member function using different enable_if conditions
(6 answers)
Closed 6 years ago.
I have a class template Bird with a Boolean template parameter can_fly. Depending on that value, I want to enable a member function with the signature void fly();.
This is my code:
#include <type_traits>
template<bool can_fly>
class Bird {
public:
template<typename void_t = typename std::enable_if<can_fly>::type>
void_t fly() { /* ... */ }
};
int main() {
Bird<true> flyingBird;
flyingBird.fly();
Bird<false> flightlessBird;
return 0;
}
This code compiles fine in Visual Studio 2015, but GCC complains that there is "no type named 'type' in 'struct std::enable_if'" in the third line of main.
I thought the fact that there is no ::type in the false case was the entire point of SFINAE. Can somebody explain to me what I did wrong and what the correct approach is?
As mentioned in this answer:
enable_if works because the substitution of a template argument resulted in an error, and so that substitution is dropped from the overload resolution set and only other viable overloads are considered by the compiler.
In your case there is no substitution because can_fly is known at the moment of instantiation. You can create a dummy default bool template parameter to make SFINAE work properly:
template<bool can_fly>
class Bird {
public:
template<bool X = can_fly, typename = typename std::enable_if<X>::type>
void fly() { /* ... */ }
};
wandbox example
Related
This question already has answers here:
Why does this substitution failure create an error?
(2 answers)
Closed 1 year ago.
I asked a question just before about why std::enable_if<false> cannot be used in SFINAE contexts, as in:
template <typename T, typename DEFAULTVOID = void>
struct TemplatedStruct {};
template <typename T>
struct TemplatedStruct<T, std::enable_if_t<false>> {}; // enable_if expression
// isn't dependent on template type, is always false and so is an error
However in the following example it is dependent on a template argument, but this also creates an error:
#include <type_traits>
template <typename value_t_arg>
struct underlyingtype
{
static inline constexpr bool bIsIntegralType =
std::is_integral_v<value_t_arg>;
template <typename T, typename DEFAULTVOID = void>
struct IsSpecialType {
static inline constexpr bool bIsSpecialType = false;
};
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
};
int main()
{
underlyingtype<int> g1; // Works
underlyingtype<double> g2; // std::enable_if_t<false, void>:
// Failed to specialize alias template
}
In the first case of using std::enable_if_t<false> it fails to compile no matter what I instantiate. However in this other case underlyingtype<int> g1; works while when I instantiate it with a double it then fails to compile, which makes me think they're two different problems.
Edit: I should mention, this fails to compile with Visual Studio Community 2019 16.9.3.
// Failed to specialize alias template
For one, there's no alias template in your code.¹ You're just delcaring bIsIntegralType to be exactly the same thing as std::is_integral_v<value_t_arg>, which is fixed (to false or true) as soon as the instantiation of underlyingtype takes place.
Therefore, the two specializations
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
are the same thing, hence clang says
Class template partial specialization 'IsSpecialType<T>' cannot be redeclared
And this is independent of what value_t_arg you pass to underlyingtype.
When removing either of the two identical specializations, the code is ok as regards underlyingtype<int> g1;, but it is still invalid upon trying to instantiate underlyingtype<double>, because value_t_arg is "blocked" to double in that case, which makes bIsIntegralType be just a false compile-time value, which in turns means that you're passing an always-and-ever-false to std::enable_if_v.
Putting it in another way, when you ask for underlyingtype<double>, the compiler starts instantiating the class underlyingtype with value_t_arg = double; at this point the compiler hasn't even looked at IsSpecialType, but it knows that bIsIntegralType == false, which makes the code for IsSpecialType's specialization invalid as per the previous question.
(¹) An alias template is a templated type alias,
template <typename T>
using new_name = old_name<T>;
whereas in your code there's no using at all, so there couldn't be a type alias, let alone an alias template.
Based on this and the previous question, it looks like you're trying to get into SFINAE and Template Meta-Programming. If I may give you a suggestion, a good way to learn it is to read and understand how the Boost.Hana library works. There's a lot of TMP and SFINAE there, but the quality of the code is high (imho) and the code itself is extremely well documented and, hence, understandable (obviously it takes time).
Consider this line:
std::cout << underlyingtype<double>::IsSpecialType<char>::bIsSpecialType << "\n";
How should we go about interpreting it?
underlyingtype is a template.
underlyingtype<double> is not a template, it is a type, a specific instantiation of underlyingtype.
underlyingtype<double>::IsSpecialType is a template, a member of a (non-template) class type underlyingtype<double> This template has a single parameter T.
underlyingtype<double>::IsSpecialType<char> is an instantiation of the preceding template.
Now, when instantiating a template, its parameters are substituted with actual arguments. Failure to perform such substitution is not an error. In case of underlyingtype<double>::IsSpecialType, the parameter is T. However std::enable_if_t<std::is_integral_v<value_t_arg>>> does not depend on T, so no substitution takes place.
This question already has answers here:
C++ function template partial specialization?
(7 answers)
Why function template cannot be partially specialized?
(4 answers)
Closed 2 years ago.
I am very new to modern C++ and am trying to do something as described below, which throws error on partial template specialization but works completely fine on complete specialization.
What am I doing here?
Language version: C++14
Compiler: GreenHills (GHS)
Doesnt work
template<bool isPrefix>
struct my_struct;
template<>
struct my_struct<true> // specializes true
{
... some data members...
char prefix_data[10]
}
template<>
struct my_struct<false> // specializes false
{
... only other data members...
// no prefix data member variable
}
template<bool isEnabled, class T>
void prefixData(const T& data)
{
// performing some operation on T
data.prefix_data[0] = 0x01;
}
// DOES NOT WORK, THROWS COMPILATION ERROR
template<class T>
void prefixData<false, T>(const T& data)
{
// no logic required here
}
However, if I specify complete specialization for the function it works.
This Works
template<>
void prefixData<false, my_struct<false>>(const T& data)
{
}
P.S: I want to avoid run-time polymorphism (abstract class + inheritance) since the code will run on embedded platform with limited resources
I want to avoid explicit specialization of each cases. I have explained here with just isPrefix in my_struct. My actual code contains more template variables.
This question already has answers here:
Member function template selection and SFINAE
(1 answer)
SFINAE not working to conditionally compile member function template
(1 answer)
Approaches to function SFINAE in C++
(2 answers)
Closed 2 years ago.
I want to switch between to members of a templatized class based on the fact the type of the template is default constructible or not.
I think I'm not far from the solution after a lot of reading and tries around std::enable_if and std::is_default_constructible, but I'm still stuck at compile-time, here's a minimized exemple :
template<typename DataType>
class MyClass{
public:
template < typename = typename std::enable_if_t<std::is_default_constructible_v<DataType>>>
inline void createNew(unsigned int index) {
new (&this->buffer[index]) DataType(); // "placement new"
}
template < typename = typename std::enable_if_t<!std::is_default_constructible_v<DataType>>>
inline void createNew(unsigned int index) {
throw BaseException("No default constructor");
}
};
This last try results on "member function already defined or declared". I think I miss something, but I don't understand why both functions are selected for compilation even if they have the exact opposite template condition.
This question already has answers here:
Checking for existence of an (overloaded) member function
(3 answers)
Closed 5 years ago.
Consider the following, I want to check if the types I pass off to some other function sf has a member function T::mf that is required by sf, I know the return type and the name but there can by any number of overloads.
After some tinkering (well it is fun..) and googling , I can get something like the code below to work, the problem is that I don't know how to express that print can have a variable number of arguments.
#include <type_traits>
#include <utility>
template <typename T,typename = void>
struct has_write : std::false_type {};
template <typename T>
struct has_write<T, decltype(std::declval<T>().write())> : std::true_type {};
template <typename T, typename R = void , typename ...Args>
struct has_print : std::false_type {};
// cant deduce, specialization never used
template <typename T, typename ...Args>
struct has_print<T, decltype(std::declval<T>().print(std::declval<Args>()...))> : std::true_type {};
struct Foo {
void write();
};
struct Bar {
int print(int, float, int);
};
int main(){
static_assert(has_write<Foo>::value, "Does not have write..");
static_assert(has_print<Bar>::value, "Does not have print..");
return 0;
}
The above compiles with g++ but the second assert fails, clang is a bit more helpful and tells me that the specializations for has_print will never be used because it cannot deduce all the types.
Since you will be calling your overload from within sf function, you should check for availability of the particular overload using the types sf function would call it with.
General checking for availability of any overload within a class would always be an XY problem by definition, because availability of any overload is never important. You need to know you can call a name with given set of arguments. Consider this: asking for availability of any overload of a given name is conceptually the same as asking if particular class has any method at all. And obviously you would not be interested in it?
An answer to a a question I had about deleting functions mentioned how member function templates can't be specialized at class scope. That led me to wonder if it's possible for a member function template specialization to have a different access level than the main template. In the code below, I'm trying to have a private specialization of a public member function template:
#include <iostream>
class Foo {
public:
template<typename T>
void func(T) { std::cout << "Public\n"; }
private:
template<>
void func<char>(char) { std::cout << "Private\n"; }
friend int main();
};
int main()
{
Foo f;
f.func(10);
f.func('a');
}
With the latest MSVC, this compiles, runs, and produces the expected output:
Public
Private
With g++ 4.8 and Clang 3.2, the code is rejected. Clang says this:
error: explicit specialization of 'func' in class scope
void func<char>(char) { std::cout << "Private\n"; }
^
Presumably g++ and Clang are using 14.7.3/2 of C++11 as the basis for their behavior, but I think there might be a little wiggle room, because 3.3.6/3 says that the global scope is a namespace, and the global namespace (indirectly) encloses the template specialization.
My question isn't about these parts of the Standard or about any of these compilers' behaviors, though, it's about whether it is possible for a member function template to have a specialization that has a different access level than the general template. For example, is it possible to have a public member function template and a private specialization of that template?
We can always do it manually.
Some random SFINAE machinery:
#include <iostream>
#include <utility>
#include <type_traits>
template<typename T> constexpr bool IsInt() { return std::is_same<T,int>::value; }
template<std::size_t>
struct SecretEnum {
enum class hidden {};
};
template<bool b, int i=1> using EnableIf = typename std::enable_if<b,typename SecretEnum<i>::hidden>::type;
class Foo {
public:
template<typename T, EnableIf< !IsInt<T>(), 1 >...>
void func(T) { std::cout << "Public\n"; }
private:
template<typename T, EnableIf< IsInt<T>(), 2 >...>
void func(T) { std::cout << "Private with int\n"; }
friend int main();
};
int main()
{
Foo f;
f.func(10);
f.func('a');
}
now this trick does not work with clang because of how I did the SFINAE and method distinguishing last I checked. But that can be replaced with other similar tricks (like pointer based default arguments in the second argument -- replace EnableIf< IsInt<T>(), 2 >... with EnableIf< IsInt<T>(), 2 >* = nullptr or somesuch for clang. I just find it less appealing.)
So what is going on above? I have two different overloads for func. Both are template functions with one argument that is a T, and a pack of some secret enum whose type is valid if and only if T matches the IsInt<T>() or !IsInt<T>() test respectively. The type of the packs differ in the two cases (one if them is SecretEnum<2>::hidden, the other is SecretEnum<1>::hidden), so their signatures are sufficiently different to satisfy most C++11 compilers (clang considers them to be identical last I checked, generating errors, I believe clang is wrong).
When you invoke func<blah>, it checks to see which (if any) of the two func are appropriate. As their conditions are exact opposites of each other, only one of them is ever the proper one.
In effect, we are doing manual specialization.
In C++1y, we may be able to template<IsInt T> and template<IsNotInt T> if the stars align properly and the concepts lite that gets into the technical report lets this work.