【问题标题】:Do non-dependent default template arguments of function templates allow for SFINAE?函数模板的非依赖默认模板参数是否允许 SFINAE?
【发布时间】:2013-03-06 13:42:08
【问题描述】:

这里的“不依赖”是指“不依赖于该特定函数模板的任何其他模板参数”。

在回答this question 时,我以为我找到了答案,但根据@Johannes(在我的回答的 cmets 中),我在这里误解了标准。举个简单的例子:

#include <type_traits>

template<class T>
struct X{
  template<class U = typename T::type>
  static void foo(int){}
  static void foo(...){}
};

int main(){
  X<std::enable_if<false>>::foo(0);
}

(Live version.)

是否可以保证上述编译成功? GCC 和 Clang 在这里不同意,正如在实时版本中切换它们时所看到的那样。但有趣的是,GCC 接受了以下内容:

#include <type_traits>

template<class T>
struct X{
  template<bool = T::f()>
  static void foo(int){}
  static void foo(...){}
};

struct Y{
  static bool f(){ return true; }
};

int main(){
  X<Y>::foo(0);
}

(Live version.)

如果T 包含constexpr 静态函数f,第二个sn-p 只会打印foo(int)。再一次,有趣的是,如果你从Y 中完全删除f(或者通过,比如说,int),GCC 会抱怨缺少一个成员,表明它不允许 SFINAE - 这与前面的矛盾观察。 Clang 接受所有变体并应用 SFINAE,我想知道这是否是标准所保证的。

(FWIW,带有 Nov CTP 的 MSVC 通常与 Clang 一致,但如果该函数存在,则在第二个 sn-p 上崩溃,可能是因为它们没有 constexpr。我提交了一个错误报告 here。 )

【问题讨论】:

    标签: c++ templates language-lawyer sfinae


    【解决方案1】:

    我认为有问题的代码是不正确的,因为当类模板被实例化时,所有成员声明都被实例化,除了成员函数和成员函数模板的定义部分和默认参数。该标准还定义了函数默认参数何时被精确实例化。

    所以默认模板参数会立即被实例化。在我看来,默认参数可能打算在这一点上包含默认模板参数的可能性非常小,因为没有描述何时会出现这样的参数稍后实例化。

    这符合“在成员的类之外出现的类模板成员的定义的模板参数列表中不应指定默认模板参数”的要求。 ,因为在实例化周围的类模板时,不可能立即实例化这样的模板参数。

    【讨论】:

    • 如果“默认模板参数”包含在“默认参数”中,那么它们也包含在实例化点的解释中,即在实例化点它的功能模板。但是,我主要想知道,因为 Clang 只是“按预期”接受所有 sn-ps。
    • @Xeo 但这极不可能,因为它只讨论函数调用。默认模板参数不仅可以用于函数调用,还可以用于任何风格的类模板实例(事实上,C++03 的唯一情况)以及函数模板的显式特化和实例化。所有这些都没有在关于隐式实例化的规则和关于实例化点的规则中的任何地方提到。我建议打开一个 Clang PR。
    • @Xeo 此外,可以在没有函数实例化的情况下使用函数默认模板参数。 decltype( &amp;X::template foo&lt;&gt; ) 之类的东西可以返回它,但该函数不会被 ODR 使用。在这种情况下要求 SFINAE 将意味着重载决议,否则不会发生。