Reputation: 518
The following doesn't compile under g++ 8.1.0 on CentOS 7:
hey.h
#pragma once
#include <iostream>
#include <type_traits>
class Valid {};
class Invalid {};
struct Hey
{
template<typename T>
static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }
template<typename T, std::enable_if_t<is_valid<T>()>* = nullptr>
void howdy() const;
};
template<typename T, std::enable_if_t<Hey::is_valid<T>()>*>
void Hey::howdy() const
{
std::cout << "Howdy" << std::endl;
}
Compiler output:
In file included from hey.cpp:1:
hey.h:18:8: error: no declaration matches ‘void Hey::howdy() const’
void Hey::howdy() const
^~~
hey.h:14:10: note: candidate is: ‘template<class T, std::enable_if_t<is_valid<T>()>* <anonymous> > void Hey::howdy() const’
void howdy() const;
^~~~~
hey.h:8:8: note: ‘struct Hey’ defined here
struct Hey
^~~
Amazingly, all I have to do to both compile correctly and get the desired behavior is add a typedef in Hey:
hey.h (fixed, first few boring lines skipped)
struct Hey
{
template<typename T>
static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }
template<typename T>
using EnableType = std::enable_if_t<is_valid<T>()>;
template<typename T, EnableType<T>* = nullptr>
void howdy() const;
};
template<typename T, Hey::EnableType<T>*>
void Hey::howdy() const
{
std::cout << "Howdy" << std::endl;
}
hey.cpp
#include "hey.h"
int main(int, char**)
{
Hey hey;
hey.howdy<Valid>();
// Adding this line breaks the build, as it should:
// hey.howdy<Invalid>();
return 0;
}
After many tweaks, I've narrowed the compiler error situation to the fact that 1) is_valid()
is a member of Hey
and 2) howdy()
is declared inside Hey
's body but defined outside. If you delete the using
and make is_valid()
a standalone function outside of Hey
, there are no issues compiling. If you delete the using
and define howdy()
inside the class definition, there are also no issue compiling. But when howdy()
is defined outside of the class definition, is_valid()
is declared inside of the class definition, and the using
isn't present, the compiler fails. Is this correct behavior? Am I looking at a compiler bug?
Upvotes: 3
Views: 1559
Reputation: 137315
The matching of expressions in template declarations is based on equivalence, a concept based on the one-definition rule. For two expressions to be considered equivalent, they must be at least token-by-token identical modulo the renaming of template parameters.
The expressions is_valid<T>()
and Hey::is_valid<T>()
are not equivalent (the second has two tokens the first doesn't have), and so the compiler is not required to match them.
Hey::EnableType<T>
is a type, and is not subject to the strict equivalence rules for expressions.
Upvotes: 5