I was learning about SFINAE and how to easy implement it with void_t. But I get different output for different compilers:
//pre c++17 void_t definition:
template<class... Ts> struct make_void {typedef void type;};
template<class... Ts> using void_t = typename make_void<Ts...>::type;
//check for member helper structures
template<class, class = void>
struct has_abc : std::false_type
{ };
template<class T>
struct has_abc<T, void_t<decltype(T::abc)>> : std::true_type
{ };
class has
{
public:
void abc();
};
class has_not
{ };
int main()
{
std::cout << has_abc<has>::value << std::endl;
std::cout << has_abc<has_not>::value << std::endl;
}
GCC 5.3.0 prints expected output 1 0, but MSVC 2015 prints 0 0, why?
EDIT:
Additional example with working GCC 5.3.0 code that supposedly violates c++ syntax:
template<class T>
void test()
{
std::cout << std::is_same<decltype(T::func), void(T::*)(void)>::value << std::endl;
}
class Test
{
public:
void func();
};
int main()
{
test<Test>();
}
Output:
1
In fact there is an error with your code. MSVC is right while GCC is simply wrong.
The syntax for pointer to member function doesn't work like that. You have to put the & in front of the expression:
//check for member helper structures
template<class, class = void>
struct has_abc : std::false_type {};
template<class T>
struct has_abc<T, void_t<decltype(&T::abc)>> : std::true_type {};
// Notice the '&' there ------^
The syntax for T::member only work with static data member, and typename T::member work for member types. While working with sfinae, it's important to distinguish small syntax properties and differences.
As a request in the comment, here's multiple statement that shows that non static member cannot be referenced without the & with GCC 5.3: https://godbolt.org/g/SwmtG2
Here's an example with GCC: http://coliru.stacked-crooked.com/a/0ee57c2c34b32753
Here's an example with MSVC: http://rextester.com/FJH22266
Here's the section from the C++ standard that explicitly state that without the &, the expression is not well formed:
[expr.prim.id]/2
An id-expression that denotes a non-static data member or non-static member function of a class can only be used:
as part of a class member access ([expr.ref]) in which the object expression refers to the member's class or a class derived from that class,
or to form a pointer to member ([expr.unary.op]), or
if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [ Example:
-
struct S {
int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK
— end example ]
As we discussed in the chat, we concluded that the reason there is a difference with the two compilers is that both GCC and MSVC has bugs that prevent this code to work well. As mentionned, MSVC will refuse to apply the SFINAE rule if there is an unrelated class that don't implement the rule correctly: http://rextester.com/FGLF68000
Note that sometime, changing the name of a type trait helped MSVC to parse my code correctly, but it's mostly unreliable.
Consider reporting a bug to microsoft and upgrade your GCC version if you want your code to work as expected.
this way works:
template<class T>
struct has_abc<T, void_t<decltype(std::declval<T>().abc())>> : std::true_type
{ };
Related
I found that the code snippet below compiles fine on gcc 11, clang, and MSVC, but starting with gcc 12.1, it gives the error:
using Y = typename Derived::template X<1>; // offending line
error: 'class Derived<T>::X<1>' resolves to 'Base<double>::X<1>' {aka 'double'}, which is not a class type
Why does the alias need to resolve to a class type for this to be a properly formed alias?
Minimal Example Code (godbolt):
#include <iostream>
template <typename T>
struct Base {
template <int I>
using X = T;
};
template <typename T>
struct Derived : public Base<T> {
using Y = typename Derived::template X<1>;
};
struct O {};
int main() {
std::cout << typeid(Derived<double>::Y).name() << std::endl; // error
std::cout << typeid(Derived<O>::Y).name() << std::endl; // works
}
In the code in question, I'm trying to create an alias to a parent's (templated) alias, but it only works when the alias resolves to a class type. Of course, something like using Y = double; is perfectly legal, and when any of Base, Derived, or X are not templated it also seems to work fine, so how come this particular combination of dependent types makes it so that X<something> is no longer allowed to resolve to e.g. arithmetic types, and only on gcc 12.1 and up?
I skimmed through the gcc 12 change list but didn't see anything that struck out to me as related.
(I'm using c++11 but it that doesn't seem to be relevant as it also occurs when specifying c++14, 17, 0x)
According to my understanding, the following two programs should be effectively identical, but the second fails to compile. Note that in this simplified example, the nesting is obviously useless, but it's necessary in the original context.
Version 1: works as expected
#include <iostream>
template<typename Value>
struct Anything {
Value value;
};
using A = const char *;
template<template<typename> class B>
struct Inner {
template<typename C>
using type = B<C>;
};
template<template<typename> class B>
struct OuterBase {
B<A> b;
void
print() { std::cout << "b: " << b.value << std::endl; }
};
using Outer = OuterBase<Inner<Anything>::type>; // this version works
int
main()
{
Outer outer;
outer.b.value = "foo";
outer.print();
return 0;
}
The compiler error in this version is "expected a template, got TypeBox<A>::Inner<Anything>::type".
Version 2: fails with compile error
#include <iostream>
template<typename Value>
struct Anything {
Value value;
};
template<typename A>
struct TypeBox {
template<template<typename> class B>
struct Inner {
template<typename C>
using type = B<C>;
};
template<template<typename> class B>
struct OuterBase {
B<A> b;
void
print() { std::cout << "b: " << b.value << std::endl; }
};
using Outer = OuterBase<Inner<Anything>::type>; // compile error here
};
int
main()
{
using Box = typename TypeBox<const char *>::Outer;
Box box;
box.b.value = "foo";
box.print();
return 0;
}
I would expect the TypeBox struct to be transparent when resolving the types inside, but according to gcc 12.2.1, it isn't. Why not?
The difference in your second example is that a compiler cannot verify at the point of definition of the template that Inner<Anything>::type is really a class template or alias template. The problem is that Inner is a nested class template and you could in theory partially or explicitly specialize Inner for the given template argument later, so that type names e.g. a type.
So compilers tend to require that you place the template keyword before the template's name in such a case to state that it really is a template:
using Outer = OuterBase<Inner<Anything>::template type>;
My understanding is that technically there is nothing in the standard requiring this use of template though, but that this wasn't entirely clear. However, the standard did allow using the template keyword in this position, but will deprecate this allowance with C++23. If I am not mistaken compilers should also allow this without template keyword in the future even for previous standard revisions (as defect report). There wasn't ever really a point in performing this test.
See also CWG issue 1478 and the paper proposing the deprecation P1787R6.
I'm trying to combine the approaches used in this answer for detecting whether a class has a member variable x with this answer to select different implementations depending on that using enable_if.
Basically, I want to write a trait class that, given a type T, provides access to the member T::x if it exists, and provides a default value otherwise.
The following code does not compile on g++: (Compiler Explorer)
#include <iostream>
#include <type_traits>
// classes with / without x member
struct WithX { static constexpr int x = 42; };
struct WithoutX {};
// trait to detect x
template <typename T, typename = void>
struct HasX : std::false_type { };
template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type { };
// trait to provide default for x
template <typename T>
struct FooTraits
{
template <bool enable = HasX<T>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <bool enable = HasX<T>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }
};
int main() {
std::cout << HasX<WithX>::value << std::endl;
// Uncomment the following line to make this compile with g++
//std::cout << HasX<WithoutX>::value << std::endl;
std::cout << FooTraits<WithoutX>::x() << std::endl;
}
g++ gives error messages that
error: 'x' is not a member of 'WithoutX'
struct HasX <T, decltype((void) T::x)> : std::true_type { };
in the part which should detect whether x is a member in the first place. Curiously though, if I uncomment the second to last line which instantiates HasX<WithoutX>::value by itself, g++ compiles without errors (Compiler Explorer).
Both clang and msvc compile without a problem on Compiler Explorer.
What's wrong here?
SFINAE only work in immediate context. In other words, if the compiler can see in advance that a declaration has a problem, then it must be an error.
When instantiating a class, the compiler will try to resolve everything it can. So is this:
template<bool enable = HasX<T>::value>
....
This is not dependent of the context of the function. This can be instantiated right when FooTraits is instantiated.
In other words, this assignation can be calculated in advance, as if you'd move it to the class scope.
In your case, the compiler has nothing to do at subtitution.
The fix is simply that:
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }
In theory, U could be a completely different type. U is immediate to the function instantiation, while T is not.
Fact that switching comment of:
//std::cout << HasX<WithoutX>::value << std::endl;
is indeed a good sign of gcc bug.
Seems gcc has issue with your form:
template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type {};
A more typical way is to use std::void_t:
template <typename T>
struct HasX <T, std::void_t<decltype(T::x)>> : std::true_type {};
which indeed solves the issue Demo.
Traits classes can be defined to check if a C++ class has a member variable, function or a type (see here).
Curiously, the ConceptTraits do not include traits to check if a C++ class defines a default constructor or given constructor?
Can traits be used to check the constructor presence?
If yes, how?
If not, why it is not possible?
Sorry for answering may own question.
Googling I have found that the actual reason we can not check if a class has constructor or a destructors is that, the known technique used to detect if a class has a member is based on taking the address of the member. But constructors and destructors have no name, we can not take the address of them.
If we can not take the address, I don't see a way to make the compiler react to a construction without instantiating it directly, but in this case there is no detection at compile time but an error.
So to answer my own question, I would say that with the current techniques it is not possible to detect them and compiler support is needed. But C++ has revealed a lot of surprises, and things that were not possible at a given time, were revealed are possible using another technique.
I hope a C++ language expert is reading that and can give a more clear explanation.
Concept Traits is no more maintained, but become part of Type Traits. And in docs of has_trivial_constructor and has_trivial_destructor, Boost authors explain clearly that compiler support is required to make this work.
A modification of Potatoswatter's answer
Works on gcc-4.6
#include <type_traits>
template< class T >
struct identity {
typedef T type;
};
template<bool, typename T, typename F>
struct if_c : identity<F> {};
template< typename T, typename F>
struct if_c<true,T,F> : identity<T> {};
template<typename Bool, typename T, typename F>
struct if_ : if_c< Bool::value, T, F> {};
template< class T >
struct is_default_constructible_;
template< class T >
struct is_default_constructible :
if_< std::is_arithmetic<T>,
std::true_type,
is_default_constructible_<T> >::type { };
template< class T >
struct is_default_constructible_ {
template<class D> class Acessible : public D
{
friend class is_default_constructible_<D>;
public:
//using D::D; may be needed once N2540 is implemented
};
template<int x>
class receive_size{};
template< class U >
static int sfinae( receive_size< sizeof Acessible<U>() > * );
template< class U >
static char sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };
};
struct p { p(); };
class q { q(); };
class r { r(int); };
#include <iostream>
using namespace std;
int main() {
cerr << is_default_constructible<int>::value << endl // outputs 1
<< is_default_constructible<p>::value << endl
<< is_default_constructible<q>::value << endl
<< is_default_constructible<r>::value << endl; // outputs 0
}
# g++-mp-4.6 --std=c++0x -Wall test.cpp && ./a.out
1
1
0
0
Warning: some analysis below is obsolete as of C++11. In C++11, access checking is done prior to instantiation and access violation is-not-an-error. Therefore the attached code may be more compliant. I haven't re-analyzed it.
I'm pretty new to SFINAE. Today it occurred to me to put a test expression inside a sizeof inside a template parameter in a function argument type.
According to N2634 this is not wrong, but highly unportable. (EDIT: appears to be compliant to C++0x FCD.) It can only return positive or fail to compile in GCC 4.2; GCC 4.5 scores a 3 out of 3 for my test cases.
The SFINAE rules were broadened (in this case) since C++03 in the FCD. New §14.8.2/8 (emphasis mine):
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. Access checking is not done as part of the substitution process. Consequently, when deduction succeeds, an access error could still result when the function is instantiated. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed.
template< class T >
class is_default_constructible {
template<int x>
class receive_size{};
template< class U >
static int sfinae( receive_size< sizeof U() > * );
template< class U >
static char sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };
};
class q { q(); };
class r { r(int); };
#include <iostream>
using namespace std;
int main() {
cerr << is_default_constructible<int>::value << endl // outputs 1
// fails to compile: access violation
// FCD demands that access violations be unrecoverable
// indeed, it's murky: q is default-constructible, but only "rarely"
//<< is_default_constructible<q>::value << endl
<< is_default_constructible<r>::value << endl; // outputs 0
}
by using std::is_default_constructible, std::is_trivially_default_constructible, std::is_nothrow_default_constructible type traits you can not only check if a type has a default constructor but you can check for the properties of it. like if it's a trivial constructor.
MSDN says that the header defines has_default_constructor and such traits.
http://msdn.microsoft.com/en-us/library/bb982179.aspx
You may want to check this code example taken from libstdc++ in Gcc 4.6.1 and which I slightly modified to work with MSVC 2010 :
/!\ : is_default_constructible returns true even if the default constructor is private or protected, i still can't find a way to solve this, any idea ?) :
namespace std {
namespace detail {
template<typename _B1, typename _B2>
struct __and_
: public conditional<_B1::value, _B2, _B1>::type
{ };
template<typename _Pp>
struct __not_
: public integral_constant<bool, !_Pp::value>
{ };
template<typename _Tp>
struct __is_array_known_bounds
: public integral_constant<bool, (extent<_Tp>::value > 0)>
{ };
template<typename _Tp>
struct __is_array_unknown_bounds
: public __and_<is_array<_Tp>, __not_<extent<_Tp>>>::type
{ };
struct __do_is_default_constructible_impl
{
template<typename _Tp>
static true_type __test(int,decltype(_Tp())* a = 0);
template<typename>
static false_type __test(...);
};
template<typename _Tp>
struct __is_default_constructible_impl
: public __do_is_default_constructible_impl
{
typedef decltype(__test<_Tp>(0)) type;
};
template<typename _Tp>
struct __is_default_constructible_atom
: public __and_<__not_<is_void<_Tp>>,
__is_default_constructible_impl<_Tp>>::type
{ };
template<typename _Tp, bool = is_array<_Tp>::value>
struct __is_default_constructible_safe;
// The following technique is a workaround for a current core language
// restriction, which does not allow for array types to occur in
// functional casts of the form T(). Complete arrays can be default-
// constructed, if the element type is default-constructible, but
// arrays with unknown bounds are not.
template<typename _Tp>
struct __is_default_constructible_safe<_Tp, true>
: public __and_<__is_array_known_bounds<_Tp>,
__is_default_constructible_atom<typename
remove_all_extents<_Tp>::type>>::type
{ };
template<typename _Tp>
struct __is_default_constructible_safe<_Tp, false>
: public __is_default_constructible_atom<_Tp>::type
{ };
} // namespace detail
/// is_default_constructible
template<typename _Tp>
struct is_default_constructible
: public integral_constant<bool, (detail::__is_default_constructible_safe<
_Tp>::value)>
{ };
}
use :
class DefaultConstructible
{
public:
DefaultConstructible() {}
};
class NotDefaultConstructible
{
public:
NotDefaultConstructible(int i) {}
};
std::is_default_constructible<DefaultConstructible>::value // -> true
std::is_default_constructible<NotDefaultConstructible>::value // -> false
After shedding much blood, sweat, and tears, I finally found a way that works on every compiler I tried:
template<class T = void> struct is_default_constructible;
template<> struct is_default_constructible<void>
{
protected:
// Put base typedefs here to avoid pollution
struct twoc { char a, b; };
template<bool> struct test { typedef char type; };
public:
static bool const value = false;
};
template<> struct is_default_constructible<>::test<true> { typedef twoc type; };
template<class T> struct is_default_constructible : is_default_constructible<>
{
private:
template<class U> static typename test<!!sizeof(::new U())>::type sfinae(U*);
template<class U> static char sfinae(...);
public:
static bool const value = sizeof(sfinae<T>(0)) > 1;
};
Traits classes can be defined to check if a C++ class has a member variable, function or a type (see here).
Curiously, the ConceptTraits do not include traits to check if a C++ class defines a default constructor or given constructor?
Can traits be used to check the constructor presence?
If yes, how?
If not, why it is not possible?
Sorry for answering may own question.
Googling I have found that the actual reason we can not check if a class has constructor or a destructors is that, the known technique used to detect if a class has a member is based on taking the address of the member. But constructors and destructors have no name, we can not take the address of them.
If we can not take the address, I don't see a way to make the compiler react to a construction without instantiating it directly, but in this case there is no detection at compile time but an error.
So to answer my own question, I would say that with the current techniques it is not possible to detect them and compiler support is needed. But C++ has revealed a lot of surprises, and things that were not possible at a given time, were revealed are possible using another technique.
I hope a C++ language expert is reading that and can give a more clear explanation.
Concept Traits is no more maintained, but become part of Type Traits. And in docs of has_trivial_constructor and has_trivial_destructor, Boost authors explain clearly that compiler support is required to make this work.
A modification of Potatoswatter's answer
Works on gcc-4.6
#include <type_traits>
template< class T >
struct identity {
typedef T type;
};
template<bool, typename T, typename F>
struct if_c : identity<F> {};
template< typename T, typename F>
struct if_c<true,T,F> : identity<T> {};
template<typename Bool, typename T, typename F>
struct if_ : if_c< Bool::value, T, F> {};
template< class T >
struct is_default_constructible_;
template< class T >
struct is_default_constructible :
if_< std::is_arithmetic<T>,
std::true_type,
is_default_constructible_<T> >::type { };
template< class T >
struct is_default_constructible_ {
template<class D> class Acessible : public D
{
friend class is_default_constructible_<D>;
public:
//using D::D; may be needed once N2540 is implemented
};
template<int x>
class receive_size{};
template< class U >
static int sfinae( receive_size< sizeof Acessible<U>() > * );
template< class U >
static char sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };
};
struct p { p(); };
class q { q(); };
class r { r(int); };
#include <iostream>
using namespace std;
int main() {
cerr << is_default_constructible<int>::value << endl // outputs 1
<< is_default_constructible<p>::value << endl
<< is_default_constructible<q>::value << endl
<< is_default_constructible<r>::value << endl; // outputs 0
}
# g++-mp-4.6 --std=c++0x -Wall test.cpp && ./a.out
1
1
0
0
Warning: some analysis below is obsolete as of C++11. In C++11, access checking is done prior to instantiation and access violation is-not-an-error. Therefore the attached code may be more compliant. I haven't re-analyzed it.
I'm pretty new to SFINAE. Today it occurred to me to put a test expression inside a sizeof inside a template parameter in a function argument type.
According to N2634 this is not wrong, but highly unportable. (EDIT: appears to be compliant to C++0x FCD.) It can only return positive or fail to compile in GCC 4.2; GCC 4.5 scores a 3 out of 3 for my test cases.
The SFINAE rules were broadened (in this case) since C++03 in the FCD. New §14.8.2/8 (emphasis mine):
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. Access checking is not done as part of the substitution process. Consequently, when deduction succeeds, an access error could still result when the function is instantiated. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed.
template< class T >
class is_default_constructible {
template<int x>
class receive_size{};
template< class U >
static int sfinae( receive_size< sizeof U() > * );
template< class U >
static char sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };
};
class q { q(); };
class r { r(int); };
#include <iostream>
using namespace std;
int main() {
cerr << is_default_constructible<int>::value << endl // outputs 1
// fails to compile: access violation
// FCD demands that access violations be unrecoverable
// indeed, it's murky: q is default-constructible, but only "rarely"
//<< is_default_constructible<q>::value << endl
<< is_default_constructible<r>::value << endl; // outputs 0
}
by using std::is_default_constructible, std::is_trivially_default_constructible, std::is_nothrow_default_constructible type traits you can not only check if a type has a default constructor but you can check for the properties of it. like if it's a trivial constructor.
MSDN says that the header defines has_default_constructor and such traits.
http://msdn.microsoft.com/en-us/library/bb982179.aspx
You may want to check this code example taken from libstdc++ in Gcc 4.6.1 and which I slightly modified to work with MSVC 2010 :
/!\ : is_default_constructible returns true even if the default constructor is private or protected, i still can't find a way to solve this, any idea ?) :
namespace std {
namespace detail {
template<typename _B1, typename _B2>
struct __and_
: public conditional<_B1::value, _B2, _B1>::type
{ };
template<typename _Pp>
struct __not_
: public integral_constant<bool, !_Pp::value>
{ };
template<typename _Tp>
struct __is_array_known_bounds
: public integral_constant<bool, (extent<_Tp>::value > 0)>
{ };
template<typename _Tp>
struct __is_array_unknown_bounds
: public __and_<is_array<_Tp>, __not_<extent<_Tp>>>::type
{ };
struct __do_is_default_constructible_impl
{
template<typename _Tp>
static true_type __test(int,decltype(_Tp())* a = 0);
template<typename>
static false_type __test(...);
};
template<typename _Tp>
struct __is_default_constructible_impl
: public __do_is_default_constructible_impl
{
typedef decltype(__test<_Tp>(0)) type;
};
template<typename _Tp>
struct __is_default_constructible_atom
: public __and_<__not_<is_void<_Tp>>,
__is_default_constructible_impl<_Tp>>::type
{ };
template<typename _Tp, bool = is_array<_Tp>::value>
struct __is_default_constructible_safe;
// The following technique is a workaround for a current core language
// restriction, which does not allow for array types to occur in
// functional casts of the form T(). Complete arrays can be default-
// constructed, if the element type is default-constructible, but
// arrays with unknown bounds are not.
template<typename _Tp>
struct __is_default_constructible_safe<_Tp, true>
: public __and_<__is_array_known_bounds<_Tp>,
__is_default_constructible_atom<typename
remove_all_extents<_Tp>::type>>::type
{ };
template<typename _Tp>
struct __is_default_constructible_safe<_Tp, false>
: public __is_default_constructible_atom<_Tp>::type
{ };
} // namespace detail
/// is_default_constructible
template<typename _Tp>
struct is_default_constructible
: public integral_constant<bool, (detail::__is_default_constructible_safe<
_Tp>::value)>
{ };
}
use :
class DefaultConstructible
{
public:
DefaultConstructible() {}
};
class NotDefaultConstructible
{
public:
NotDefaultConstructible(int i) {}
};
std::is_default_constructible<DefaultConstructible>::value // -> true
std::is_default_constructible<NotDefaultConstructible>::value // -> false
After shedding much blood, sweat, and tears, I finally found a way that works on every compiler I tried:
template<class T = void> struct is_default_constructible;
template<> struct is_default_constructible<void>
{
protected:
// Put base typedefs here to avoid pollution
struct twoc { char a, b; };
template<bool> struct test { typedef char type; };
public:
static bool const value = false;
};
template<> struct is_default_constructible<>::test<true> { typedef twoc type; };
template<class T> struct is_default_constructible : is_default_constructible<>
{
private:
template<class U> static typename test<!!sizeof(::new U())>::type sfinae(U*);
template<class U> static char sfinae(...);
public:
static bool const value = sizeof(sfinae<T>(0)) > 1;
};