Следующее не компилируется под g ++ 8.1.0 на 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;
}
Выход компилятора:
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
^~~
Удивительно, но все, что мне нужно сделать, чтобы правильно скомпилировать и получить желаемое поведение, это добавить typedef в Hey:
hey.h (исправлено, первые скучные строки пропущены)
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;
}
После многих настроек я сузил ситуацию с ошибкой компилятора до того, что 1) is_valid()
является членом Hey
и 2) howdy()
объявлен внутри Hey
Тело, но определено снаружи. Если вы удалите using
и сделать is_valid()
автономная функция вне Hey
Нет проблем с компиляцией. Если вы удалите using
и определить howdy()
внутри определения класса также нет проблем с компиляцией. Но когда howdy()
определяется вне определения класса, is_valid()
объявляется внутри определения класса, а using
нет, компилятор выходит из строя. Это правильное поведение? Я смотрю на ошибку компилятора?
Соответствие выражений в объявлениях шаблона основано на эквивалентность, концепция, основанная на правиле одного определения. Чтобы два выражения считались эквивалентными, они должны быть идентичны токену по токену по модулю переименования параметров шаблона.
Выражения is_valid<T>()
а также Hey::is_valid<T>()
не эквивалентны (у второго есть два токена, у первого нет), поэтому компилятору не требуется их сопоставлять.
Hey::EnableType<T>
является типом и не подчиняется строгим правилам эквивалентности выражений.
Других решений пока нет …