3

This question is related to this answer.

In this example SFINAE uses variable template has_literal_x specialization instead of the base template:

struct A { };
A operator"" _x(char const*) { return {}; }

template<typename T, typename S, typename = void>
constexpr bool has_literal_x = false;

template<typename T, typename S>
constexpr bool has_literal_x<T, S, 
    std::enable_if_t<
        std::is_same<
            decltype(operator""_x(std::declval<S>())), T
            >::value
        >
    > = true;


int main()
{  
    std::cout << has_literal_x<A, char const*> << std::endl; // 1
}

And here it uses the base template:

template<typename T, typename S, typename = void>
constexpr bool has_literal_x = false;

template<typename T, typename S>
constexpr bool has_literal_x<T, S, 
    std::enable_if_t<
        std::is_same<
            decltype(operator""_x(std::declval<S>())), T
            >::value
        >
    > = true;

struct A { };
A operator"" _x(char const*) { return {}; }

int main()
{  
    std::cout << has_literal_x<A, char const*> << std::endl; // 0
}

On both GCC (first, second) and Clang (first, second) order of defining templates and user literal changes which overload is chosen by SFINAE. Why?

Community
  • 1
  • 1
xinaiz
  • 7,744
  • 6
  • 34
  • 78
  • 2
    "*It happens*" What happens? – ildjarn Sep 29 '16 at 22:56
  • @ildjarn Sorry for that, it is late here and my brain can't create comprehensible sentences :) Edited. – xinaiz Sep 29 '16 at 23:06
  • Data point: if I drop the `S` template parameter (just replacing it with a `const char *`), I get a compilation error for the second case: `error: there are no arguments to ‘operator""_x’ that depend on a template parameter, so a declaration of ‘operator""_x’ must be available [-fpermissive]`. I am staring at the "declaration of ... must be available", and can't help but think this is a clue. – Sam Varshavchik Sep 29 '16 at 23:34
  • @SamVarshavchik But in the examples `there are arguments that depend on template parameter` :(. Shouldn't it be checked in the moment of instantiation instead of template declaration? If not, we would have to include custom "type traits" always after another includes... – xinaiz Sep 29 '16 at 23:36
  • I know what is in the examples. I can read them just fine. The point is that the operator must be defined before use. Even though by the virtue of a template parameter the template definition compiles, I'm thinking that this simply postpones the issue until template instantiation, at which point SFINAE works according to its principles. Template subsittution fails, the general template gets used. – Sam Varshavchik Sep 29 '16 at 23:41

1 Answers1

4

This is a variant of the bog-standard two-phase lookup question. For dependent function names,

  • Unqualified lookup considers only the template definition context
  • Argument-dependent lookup considers both the template definition context and the template instantiation context.

For your second case, unqualified lookup in the template definition context finds nothing, and there's no ADL for const char *.

T.C.
  • 133,968
  • 17
  • 288
  • 421