C++ 为什么SFINAE在这种情况下对我不正确,以及如何修复它?

C++ 为什么SFINAE在这种情况下对我不正确,以及如何修复它?,c++,templates,c++14,sfinae,C++,Templates,C++14,Sfinae,我试图在struct A中留下一个函数foo(打印0),如果它的参数有template methodisA,另一个函数(打印1)没有。这段代码(简化为下面的最小示例)编译(使用gcc 6.1.0和clang-3.9.0,使用显式--std=c++14选项进行了尝试)并运行 但它会打印1,但我确信,它会打印0。我想知道我错在哪里,但真正的问题是:如何使这项工作正确 请仅使用C++14解决方案 #include <type_traits> #include <iostream>

我试图在
struct A
中留下一个函数
foo
(打印0),如果它的参数有template method
isA
,另一个函数(打印1)没有。这段代码(简化为下面的最小示例)编译(使用gcc 6.1.0和clang-3.9.0,使用显式
--std=c++14
选项进行了尝试)并运行

但它会打印1,但我确信,它会打印0。我想知道我错在哪里,但真正的问题是:如何使这项工作正确

请仅使用C++14解决方案

#include <type_traits>
#include <iostream>
#include <utility>

using std::enable_if;
using std::declval;
using std::true_type;
using std::false_type;
using std::cout;

template<int M>
struct ObjectX
{
  template<typename C>
  bool isA() { return false; }
};

struct XX : ObjectX<23456> {
  int af;
};

template <typename ObjType> using has_dep = decltype(declval<ObjType>().template isA<void>());

template <typename, typename = void>
struct has_isa : public false_type {};

template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};

template<typename ObjType>
struct A
{
  template<typename T = void>
  typename enable_if<has_isa<ObjType>::value, T>::type
  foo() {
    cout << "called foo #0" << "\n";
  }

  template<typename T = void>
  typename enable_if<!has_isa<ObjType>::value, T>::type
  foo() {
    cout << "called foo #1" << "\n";
  }
};

int
main()
{
  A<XX> axx;
  // XX().template isA<void>(); -- to check, that we can call it and it exists
  axx.foo();
  return 0;
}
#包括
#包括
#包括
使用std::enable_if;
使用std::declval;
使用std::true_类型;
使用std::false_类型;
使用std::cout;
模板
结构对象X
{
模板
bool isA(){return false;}
};
结构XX:ObjectX{
int af;
};
使用has_dep=decltype(declval().template isA())的模板;
模板
结构有_isa:public false_type{};
模板
结构具有_isa:public true _type{};
模板
结构A
{
模板
typename启用_if::type
foo(){
cout-axx;
//XX().template isA();--为了检查,我们可以调用它并且它存在
axx.foo();
返回0;
}

您的sfinae失败,因为
的isa
选择了错误的专业化

的使用必须是默认实现或专用版本

根据定义,您有一个要作废的默认参数:

//   default argument ---------v
template <typename, typename = void>
struct has_isa : public false_type {};
嘿,那类型
has_dep
t.isA()
的返回类型,它是
bool

因此,专用版本如下所示:

template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType>> : public true_type {};
//                      ^--- really, this is bool in our case
template<typename T = ObjType>
typename enable_if<has_isa<T>::value, void>::type
foo() { //                 ^--- sfinae happens with T
  cout << "called foo #0" << "\n";
}

这样,<代码> Hasyasi总是考虑专业化,因为我们把<代码> Value>代码>作为第二个模板参数,现在我们的专业化总是以<代码> Value<代码>为第二个参数。


此外,正如Barry所说,由于sfinae仅出现在直接上下文中,因此函数的形式不正确。您应该这样写:

template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType>> : public true_type {};
//                      ^--- really, this is bool in our case
template<typename T = ObjType>
typename enable_if<has_isa<T>::value, void>::type
foo() { //                 ^--- sfinae happens with T
  cout << "called foo #0" << "\n";
}
模板
typename启用_if::type
foo(){/^---sfinae与T一起发生

cout您的问题是您专门化了错误的类:

您应该强制
has_dep
返回
void

template <typename ObjType> using has_dep = decltype(static_cast<void>(declval<ObjType>().template isA<void>()));
使用has_dep=decltype(static_cast(declval().template isA())的模板;
所以这里

template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};
// It is really <bjType, void> you specialize.
模板
结构具有_isa:public true _type{};
//真的是你的专长。

此程序中有两个问题


首先,
has_dep
bool
。当我们尝试
has_dep
时,添加默认模板参数意味着这实际上是
has_dep
。但是专门化是
has_dep
,这与我们实际查找的内容不匹配。
bool
void
不匹配。这就是
has_dep的原因de>是
false\u type
。解决这个问题的方法是,我建议你仔细阅读Q/A,了解它的工作原理。在你的专业中,你需要使用
void\u t


第二,这是不对的:

template<typename T = void>
typename enable_if<has_isa<ObjType>::value, T>::type
模板
typename启用_if::type
SFINAE仅发生在替换的直接上下文中,而类模板参数不在函数模板替换的直接上下文中。正确的模式如下:

template <typename T = ObjType> // default to class template parameter
enable_if_t<has_isa<T>>         // use the function template parameter to SFINAE
foo() { ... }
template//默认为类模板参数
如果启用,请使用函数模板参数
foo(){…}


完成这两个修复后,程序将按预期工作。

has_isa
不会自动尝试使用
has_dep
而不是使用
void
实例化专门化,如果您没有提供第二个模板参数。@Albjenow如果函数成员不是模板,则此方法有效。请尝试对代码进行细微更改(删除模板C并修复它的dep),您将看到其他一切都是正确的。它将尝试这样做,因为更多限制的专门化总是战胜更少限制的void\t是C++17,但我有了这个想法,谢谢,我肯定可以编写我自己的包装器,类似于它
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};
// It is really <bjType, void> you specialize.
template<typename T = void>
typename enable_if<has_isa<ObjType>::value, T>::type
template <typename T = ObjType> // default to class template parameter
enable_if_t<has_isa<T>>         // use the function template parameter to SFINAE
foo() { ... }