C++ 是否有一种基于当前类中可用重载的SFINAE方法?

C++ 是否有一种基于当前类中可用重载的SFINAE方法?,c++,c++11,templates,clang,sfinae,C++,C++11,Templates,Clang,Sfinae,我已经使用这样的代码有一段时间了(至少从GCC 4.9/Clang 3.5开始): 有没有一种方法不依赖于在自己的定义中使用不完整的类型来实现这一点?快速而简单的方法是在基类中定义bar #include <utility> template<typename child> struct base { void bar(int); }; struct foo : base<foo> { template<typename R,

我已经使用这样的代码有一段时间了(至少从GCC 4.9/Clang 3.5开始):


有没有一种方法不依赖于在自己的定义中使用不完整的类型来实现这一点?

快速而简单的方法是在基类中定义
bar

#include <utility>

template<typename child>
struct base {
    void bar(int);
};

struct foo : base<foo> {
    template<typename R,
              typename = decltype(std::declval<base<foo>>().bar(std::begin(std::declval<R>())))>
    void bar(const R& range);
};
如果
bar
受到约束,则可以使用相同的约束:

struct foo {
    template<typename T, std::enable_if_t<some_contraint<T>::value>* = 0>
    void bar(T);

    template<typename R,
        std::enable_if_t<some_contraint<*std::begin(std::declval<R>())>::value>* = 0>
    void bar(const R& range);
};
structfoo{
模板
空心钢筋(T);
模板
空栏(常数R和范围);
};
最后,如果您喜欢最后两个选项,可以将范围约束封装在类型特征中:

template<typename, typename = void>
struct is_valid_range : std::false_type {};

template<typename T>
struct is_valid_range<T, std::enable_if_t<some_contraint<*std::begin(std::declval<R>())>::value>> : std::true_type {};
模板
结构是有效的\u范围:std::false\u类型{};
模板
struct是\有效\范围:std::true \类型{};

看起来您正试图使其在函数体无法编译时不会选择重载。问题是编译器需要确保签名在进入正文之前已编译

相反,根据您需要能够更具体地使用
R
做什么来确定SFINAE'ing呢?例如:

template<typename R,
         class = decltype(begin(std::declval<const R&>())),
         class = decltype(end(std::declval<const R&>()))>
void bar(const R& range);
模板
空栏(常数R和范围);
这样,仅当您可以对类型f
const R&
调用
begin
end
时,才会选择此重载;
class Foo;
void free_bar(Foo* foo, int n){
  (void)foo;
  std::cout << n << "\n";
}

class Foo {
public:
  template<class X>
  void bar(X&& x) {
    return free_bar( this, std::forward<X>(x) );
  }
};

template <typename R>
auto free_bar(Foo* foo, const R& range)
-> decltype( free_bar( foo, *std::begin(range) ) )
{
  for (auto&&x:range)
    free_bar(foo, decltype(x)(x));
}
无效自由条(Foo*Foo,int n){ (无效)富;
std::cout也许您可以将foo设置为其他模板参数的默认值:

#include <utility>

class foo
{
public:
    void bar(int n);

    template <typename R,
              typename F = foo,
              typename = decltype(std::declval<F>().bar(*std::begin(std::declval<R>())))>
    void bar(const R& range);
};
#包括
福班
{
公众:
空条(int n);
模板
空栏(常数R和范围);
};


这将延迟检查
foo
是否已完成。

该类在函数默认参数中已完成,因此您可以将SFINAE移动到具有默认参数的函数参数中。目标是阻止该模板如此剧烈地匹配。作为一个具体示例,
std::string
char的容器de>s,但是我没有为
char
设置重载,所以模板被跳过,它会选择
string\u view
重载。如果没有SFINAE,它会选择模板,然后出错。您还希望
std::vector
正常工作吗?其次,您似乎缺少了
*
@Yakk,理想情况下是的。修复了该缺失ng
*
。我想做的事情你是对的。函数的主体类似于(自动e:range)bar(e)
所以我需要检查
bar(e)
是否有效,而不仅仅是
R
是否是范围类型。@TavianBarnes以及类型需要调用
bar(e)什么
?您仍然可以检查迭代器类型
R
上的那些约束。约束只是该类型的
bar
重载是否存在于
foo
@TavianBarnes中。那么,如果您使用无重载元素的
向量
调用
bar
,会发生什么呢编译错误?如果是这样的话,我会允许编译错误发生。不,如果可能的话,它应该选择其他一些重载。可能还有另一个可行的
bar()
重载,这将比
const R&
绑定得不那么紧密,因为需要用户定义的转换。在这种情况下,它应该使用该重载,而不是出错。小心,有人可能会覆盖typename
F
的值。您可以在它前面引入一个变量,以防万一。
template<typename R,
         class = decltype(begin(std::declval<const R&>())),
         class = decltype(end(std::declval<const R&>()))>
void bar(const R& range);
class Foo;
void free_bar(Foo* foo, int n){
  (void)foo;
  std::cout << n << "\n";
}

class Foo {
public:
  template<class X>
  void bar(X&& x) {
    return free_bar( this, std::forward<X>(x) );
  }
};

template <typename R>
auto free_bar(Foo* foo, const R& range)
-> decltype( free_bar( foo, *std::begin(range) ) )
{
  for (auto&&x:range)
    free_bar(foo, decltype(x)(x));
}
#include <utility>

class foo
{
public:
    void bar(int n);

    template <typename R,
              typename F = foo,
              typename = decltype(std::declval<F>().bar(*std::begin(std::declval<R>())))>
    void bar(const R& range);
};