C++ 当有两个具有不同签名的函数时,为什么SFINAE会导致失败?
我试图把我的头绕在这个问题上,因为它是这样写的,它隐藏了它实际上在做什么。因此,我将其改写为:C++ 当有两个具有不同签名的函数时,为什么SFINAE会导致失败?,c++,c++11,sfinae,C++,C++11,Sfinae,我试图把我的头绕在这个问题上,因为它是这样写的,它隐藏了它实际上在做什么。因此,我将其改写为: template<typename CLASS> struct has_begin { // NOTE: sig_matches() must come before fn_exists() as it is used for its // type. Also, no function bodies are needed as they are never
template<typename CLASS>
struct has_begin
{
// NOTE: sig_matches() must come before fn_exists() as it is used for its
// type. Also, no function bodies are needed as they are never called.
// This matching sig results in a return type of true_type
template<typename A_CLASS>
static auto
sig_matches(void(A_CLASS::*)())
-> std::true_type;
// If the member function A_CLASS::begin exists and a sig_matches() function
// exists with the required sig, then the return type is the return type of
// sig_matches(), otherwise this function can't exist because at least one
// the types don't exist so match against fn_exists(...).
template <typename A_CLASS>
static auto
fn_exists(decltype(&A_CLASS::begin))
-> decltype(sig_matches<A_CLASS>(&A_CLASS::begin));
// Member function either doesn't exist or doesn't match against a
// sig_matches() function.
template<typename A_CLASS>
static auto
fn_exists(...)
-> std::false_type;
// Intermediate storage of type for clarity
typedef decltype(fn_exists<CLASS>(nullptr)) type;
// Storing the resulting value
static int const value = type::value;
};
为什么它无法与C::begin()
匹配
编辑
原因是&A_CLASS::begin
不明确。更正的等级如下:
template<typename CLASS>
struct has_begin
{
// NOTE: No function bodies are needed as they are never called.
// If the member function A_CLASS::begin exists with the required sig,
// then the return type is true_type otherwise this function can't
// exist because the type cannot be deduced.
template <typename A_CLASS>
static auto
fn_exists(decltype((void(A_CLASS::*)())&A_CLASS::begin))
-> std::true_type;
// Member function either doesn't exist or doesn't match against the
// required signature
template<typename A_CLASS>
static auto
fn_exists(...)
-> std::false_type;
// Intermediate storage of type for clarity
typedef decltype(fn_exists<CLASS>(nullptr)) type;
// Storing the resulting value
static int const value = type::value;
};
模板
结构已开始
{
//注意:不需要函数体,因为它们从未被调用。
//如果成员函数A_CLASS::begin具有所需的sig,
//则返回类型为true\u type,否则此函数无法
//存在,因为无法推断类型。
模板
静态自动
fn_存在(decltype((void(A_类::*)())和A_类::begin))
->std::真_型;
//成员函数不存在或与
//所需签名
模板
静态自动
fn_存在(…)
->std::假_型;
//为清晰起见,中间存储类型为
typedef decltype(fn_exists(nullptr))类型;
//存储结果值
静态int const value=type::value;
};
雅克和戴普提出了一个很好的观点。以下是一种使用兼容签名执行相同操作的方法:
template<typename CLASS>
struct has_begin
{
// NOTE: No function bodies are needed as they are never called.
// If the member function A_CLASS::begin exists that has a compatible sig,
// then the return type is true_type otherwise this function can't exist
// because the type cannot be deduced.
template <typename A_CLASS>
static auto
fn_exists(decltype(std::declval<A_CLASS>().begin())*)
-> std::true_type;
// Member function either doesn't exist or doesn't match against the
// required compatible signature
template<typename A_CLASS>
static auto
fn_exists(...)
-> std::false_type;
// Intermediate storage of type for clarity
typedef decltype(fn_exists<CLASS>(nullptr)) type;
// Storing the resulting value
static int const value = type::value;
};
模板
结构已开始
{
//注意:不需要函数体,因为它们从未被调用。
//如果存在具有兼容sig的成员函数A_CLASS::begin,
//则返回类型为true\u type,否则此函数不存在
//因为类型无法推断。
模板
静态自动
fn_存在(decltype(std::declval().begin())*)
->std::真_型;
//成员函数不存在或与
//所需的兼容签名
模板
静态自动
fn_存在(…)
->std::假_型;
//为清晰起见,中间存储类型为
typedef decltype(fn_exists(nullptr))类型;
//存储结果值
静态int const value=type::value;
};
我发现这个答案比Yakks更清晰,因为它不需要详细的名称空间和其他“噪音”,而是YYMV。替换
// If the member function A_CLASS::begin exists and a sig_matches() function
// exists with the required sig, then the return type is the return type of
// sig_matches(), otherwise this function can't exist because at least one
// the types don't exist so match against fn_exists(...).
template <typename A_CLASS>
static auto
fn_exists(decltype(&A_CLASS::begin))
-> decltype(sig_matches<A_CLASS>(&A_CLASS::begin));
这是C++11。停止做那些C++03体操
// bundle of types:
template<class...>struct types{using type=types;};
// comes in std in C++14 or 1z, but easy to write here:
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
// hide the SFINAE stuff in a details namespace:
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,void_t<
Z<Ts...>
>>:std::true_type{};
}
// can_apply<template, types...> is true
// iff template<types...> is valid:
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,types<Ts...>>;
将使您的测试失败,但通过我的测试。
decltype(&A_-CLASS::begin)
只有在&A_-CLASS::begin
引用单个函数时才是格式良好的。在C
的情况下,它指的是一个重载集,它没有单一的类型。请参见[over.over]/1检查函数调用表达式的格式是否正确通常比检查是否可以形成指向该函数的指针更容易。例如,template static auto fn_exists(std::nullptr_t)->decltype(std::declvalIf在替换之后,它无法编译模糊的重载。到party@DarioOO有点晚了。;)我不想指出点=)我更喜欢简洁的注释eah,只是想出来了。谢谢。看起来你做的体操比我做的多得多。我觉得这很难理解,但我会更仔细地研究。@Adrian我收拾了一些烂摊子。现在写can\u apply
有点乱,但是一旦完成了(并且可以在实用程序头中完成一次),写begin\u result
和has\u begin
每行1-2行。这比我写的有什么好处?而且,我实际上需要的是准确的,而不仅仅是兼容的签名。你能记住这一点吗?@Adrian Yours检查一组特定的符号是否生成指向具有特定签名的方法的指针。我会检查您是否可以执行类似于使用特定签名调用方法的操作。如果您确实需要特定的语法来生成特定的方法指针,那么您的方法是最好的:在我看来,这似乎是一个奇怪的情况。如果您需要代码来调用具有特定参数集的特定方法,那么我的方法是最好的,因为我正在测试将要使用的方法,而不是通常与将要使用的方法兼容的方法。
// If the member function A_CLASS::begin exists and a sig_matches() function
// exists with the required sig, then the return type is the return type of
// sig_matches(), otherwise this function can't exist because at least one
// the types don't exist so match against fn_exists(...).
template <typename A_CLASS>
static auto
fn_exists(std::nullptr_t)
-> decltype(sig_matches<A_CLASS>(&A_CLASS::begin));
decltype(&A_CLASS::begin) is ambiguous when there are overloads for `begin`.
// bundle of types:
template<class...>struct types{using type=types;};
// comes in std in C++14 or 1z, but easy to write here:
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
// hide the SFINAE stuff in a details namespace:
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,void_t<
Z<Ts...>
>>:std::true_type{};
}
// can_apply<template, types...> is true
// iff template<types...> is valid:
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,types<Ts...>>;
// the type of X.begin(), in a SFINAE friendly manner:
template<class X>
using begin_result = decltype( std::declval<X>().begin() );
// Turn the above into a "is there a begin" test:
template<class X>
using has_begin = can_apply< begin_result, X >;
struct foo {
void begin(double x = 0.0) {}
};