出于某种原因,我可以看到最顶层template<typename T> X<...>::fn(T&&)
,但不能看到基类版本。使用using
关键字导入它们不起作用。据我了解:
In Class definition
示例代码:
#include <iostream>
#include <type_traits>
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0
struct dummy {};
template<typename T>
struct always_false : std::false_type {};
template<typename...Ts>
struct X;
template<typename Tx, typename T, typename...Ts>
struct X<Tx, T, Ts...> : X<Tx, Ts...>
{
using X<Tx, Ts...>::fn;
template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
auto fn(R&& x)
{
return x;
}
};
template<typename Tx>
struct X<Tx>
{
template<typename R>
auto fn(R&& x)
{
static_assert(always_false<R>::value, "Invalid type");
}
};
int main()
{
X<dummy, int, float> x;
std::cout << x.fn(1) << std::endl;
std::cout << x.fn(1.f) << std::endl;
std::cout << "Hello, world!\n";
}
我在g ++,clang和VC ++上尝试了这一点,它们都有各种错误,(ambiguous call,member function disabled和could not deduce)。值得注意的是,g ++在调用X<dummy, int, float>::fn(int&&)
时失败,而clang和VC ++在调用X<dummy, int, float>::fn(float&&)
时失败。
据我了解,编译器在调用template<typename R> R X<dummy>::fn(R&&)
时应忽略绝对基类成员函数X<dummy, int, float>::fn(float&&)
,因为该模板应解析为float X<dummy>::fn(float&&)
,这与派生成员完全匹配函数float X<dummy, float>::fn(float&&)
要求在没有歧义的情况下调用派生函数。
我做错了什么?我不明白的是什么?
用T.C.'s answer so far来解释,“这就是规范所说的”,我想说这不是正确的解释。给出的两点相互冲突。如果它们是一个同样好的匹配(第1点),那么只有最衍生的函数签名应该是可见的(第2点)。
无论如何,如果问题是规范问题,那么如果我要禁用匹配重载的可能性会导致模糊,那么它应该消失。因此,以下应该有效:
#include <iostream>
#include <type_traits>
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0
template<typename T>
struct always_false : std::false_type {};
template<typename...Ts>
struct list {};
template<typename...Ts>
struct is_one_of;
template<template <typename...> class TT, typename T, typename T1, typename...Ts>
struct is_one_of<T, TT<T1, Ts...>> : is_one_of<T, TT<Ts...>> {};
template<template <typename...> class TT, typename T, typename...Ts>
struct is_one_of<T, TT<T, Ts...>> : std::true_type {};
template<template <typename...> class TT, typename T>
struct is_one_of<T, TT<>> : std::false_type {};
template<typename...Ts>
struct X;
template<typename L, typename T, typename...Ts>
struct X<L, T, Ts...> : X<L, Ts...>
{
using X<L, Ts...>::fn;
template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
constexpr auto fn(R&& x) const
{
return x;
}
};
template<typename L>
struct X<L>
{
template<typename R, ENABLE_IF(!is_one_of<R, L>::value)>
constexpr auto fn(R&& x) const
{
static_assert(always_false<R>::value, "Type R didn't match");
}
};
template<typename...Ts>
struct XX : X<list<Ts...>, Ts...> {};
int main()
{
XX<int, float> x;
std::cout << x.fn(1) << std::endl;
std::cout << x.fn(2.f) << std::endl;
}
答案 0 :(得分:1)
两件事:
您的基准全能版fn
与其他fn
品种的匹配度相同;因此,充其量只会导致模糊的过载错误。
隐藏 using-declaration 并不考虑完整签名(函数模板将包含模板参数列表)。它only considers 1)name,2)(function)parameter-type-list,3)cv-qualification,和4) ref-qualifier (如果有的话)。如果所有四个匹配,则基类功能模板被隐藏,而不是由 using-declaration 引入。值得注意的是,不考虑模板参数列表。在您的情况下,各种fn
之间唯一不同的是模板参数列表;它们都具有相同的名称,相同的参数类型列表,相同的cv资格和相同的ref-qualifier(或缺少它们)。因此,派生最多的fn
将隐藏基类中的所有fn
。
GCC似乎没有将此部分实现为规范,并在决定隐藏时考虑模板参数列表。
此部分的一个可能的解决方法是将enable_if
移动到一个函数参数,隐藏检查会考虑 。
C ++中的重载解析方式是:
隐藏在第一步操作:如果隐藏声明,则不会通过名称查找找到它,因此它不在初始候选集中,并且在任何情况下都不会被重载解析视为< / em>,无论在步骤2,3或4中发生了什么。为了解决重载问题,实际上不存在隐藏声明。
因此,在您的情况下,基类fn
都是隐藏的。这是什么意思?这意味着通过名称查找找到的唯一候选者是来自最派生类的int
候选者,没有别的。如果模板参数推断和替换成功,则将调用该函数模板。如果它们失败(如在x.fn(2.f)
情况下),则没有可行的候选者,并且您会收到错误。