Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if non-member function that accepts T param exists

Tags:

c++

c++11

sfinae

I want to check if a non-member function that accepts a T parameter type exists. To do so I used void_t "trick" presented by Mr. Walter E. Brown at cppcon(same trick works without any problems to check if a member type or member function exists).

#include <iostream>
#include <type_traits>

template<typename...>
using void_t = void;

void Serialize(float&)
{
}

template<typename T, typename = void>
struct has_external_serialize : std::false_type
{
};

template<typename T>
struct has_external_serialize<T, void_t<decltype(Serialize(std::declval<T&>()))>> : std::true_type
{
};

void Serialize(int&)
{
}

int main(int argc, const char * argv[])
{
    std::cout<<has_external_serialize<float>::value<<has_external_serialize<int>::value;
}

This code prints 11 when compiled using GCC and 10 when compiled with clang(xcode 5.1.1).

My questions is - is this code correct? If yes, is there a bug in clang or a bug in GCC or the code is in some "implementation defined" area and I can't assume it will have same behaviour on all platforms?

like image 491
Mircea Ispas Avatar asked Sep 10 '14 22:09

Mircea Ispas


1 Answers

The discrepancy between the compilers is caused by the definition of void_t: Is there a compiler bug exposed by my implementation of an is_complete type trait? In short, the standard was unclear whether unused arguments in alias template specializations could result in substitution failure or are simply ignored. The resolution to CWG issue 1558 clarifies that the shorter definition of void_t in the question should work.

With that issue worked around using

template<typename... Ts>
struct make_void { typedef void type;};

template<typename... Ts>
using void_t = typename make_void<Ts...>::type;

both compilers produce 10.

§14.6.4.2 [temp.dep.candidate]:

For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:

  • For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
  • For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.

If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.

Unqualified lookup for Serialize is performed in template definition context and will not find Serialize(int &), and there's no ADL for an argument of type int&, so 10 is the correct output.

like image 184
T.C. Avatar answered Oct 22 '22 06:10

T.C.