【问题标题】:'using' declaration as SFINAE“使用”声明为 SFINAE
【发布时间】:2015-03-29 17:48:51
【问题描述】:

我可以在从模板类私有派生时使用 SFINAE(或其他技术)声明 using 吗? 为了更好地理解,请参见下面的代码:

#include <iostream>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

template <class T>
struct D : private T {
    using T::f;
    // using T::g; // need this only if T provides g() function
};

int main() {
    D<S1>().f(); // ok. Prints 'S1::f'
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // fail. But wants to be ok and prints 'S2::g'
    return 0;
}

我怎样才能达到期望的行为(如果可能的话)?

【问题讨论】:

标签: c++ sfinae using-declaration private-inheritance


【解决方案1】:

Bryan Chen 的答案的变体看起来更丑,但更容易扩展到多个检查,并且不需要复制 D&lt;type-with-f&gt;D&lt;type-without-f&gt; 之间共享的代码,是使用继承链,其中每一步都会检查一个额外的成员。唯一需要的重复是构造函数的继承,如果合适的话。

struct A {
  void f() { }
  void g() { }
  void i() { }
};

// The generic case. D<T, char[N]> simply provides what D<T, char[N+1]> provides.
template <typename T, typename U = char[1]>
struct D : D<T, char[sizeof(U) + 1]> {
  using D<T, char[sizeof(U) + 1]>::D;
};

// The end of the chain. This is where T gets inherited. It declares all of its own
// specialisations as its friends, so that they can access other members of T.
template <typename T>
struct D<T, char[6]> : private T {
  template <typename, typename>
  friend struct D;

  D(int) { }
  void fun() { }
};

// Check for T::f.
template <typename T>
struct D<T, char[2 + !sizeof(&T::f)]> : D<T, char[3]> {
  using D<T, char[3]>::D;
  using T::f;
};

// Check for T::g.
template <typename T>
struct D<T, char[3 + !sizeof(&T::g)]> : D<T, char[4]> {
  using D<T, char[4]>::D;
  using T::g;
};

// Check for T::h.
template <typename T>
struct D<T, char[4 + !sizeof(&T::h)]> : D<T, char[5]> {
  using D<T, char[5]>::D;
  using T::h;
};

// Check for T::i.
template <typename T>
struct D<T, char[5 + !sizeof(&T::i)]> : D<T, char[6]> {
  using D<T, char[6]>::D;
  using T::i;
};

int main() {
  D<A> d = 4; // ok: verify that constructors got inherited
  // A &a = d; // error: verify that inheritance of A is private
  d.f(); // ok: verify that f got inherited
  d.g(); // ok: verify that g got inherited
  // d.h(); // error: verify that h is not available
  d.i(); // ok: verify that i got inherited
  d.fun(); // ok: verify that the inheritance chain didn't get broken
}

注意:您可能想用std::declval&lt;T&gt;().f() 代替检查&amp;T::f,而不是检查。前者不能处理重载函数。

【讨论】:

  • 嗯,我可能用过std::integral_constant,但非常好。
  • @T.C.嘿,这是我知道的其中一件事,但不知何故永远不会使用,因为只要它有用,我就已经在脑海中找到了一个不需要它的解决方案。 :)
【解决方案2】:

C++ 部分模板特化并在 SFINAE 中使用 decltype(void(&amp;T::g))

#include <iostream>
#include <type_traits>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

template <class T, class V = void>
struct D : private T {
    using T::f;
};

template <class T>
struct D<T, decltype(void(&T::g))> : private T {
    using T::f;
    using T::g; // need this only if T provides g() function
};

int main() {
    D<S1>().f(); // ok. Prints 'S1::f'
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // ok. Prints 'S2::g'
    return 0;
}

Live Demo


编辑:

这是另一种更灵活的方法,但我不知道private virtual 继承如何与实际用例一起使用。请让我知道它是否会导致任何问题(例如 UB)。

#include <iostream>
#include <type_traits>

struct S1 {
    void f() { std::cout << "S1::f\n"; }
};

struct S2 {
    void f() { std::cout << "S2::f\n"; }
    void g() { std::cout << "S2::g\n"; }
};

struct S3 {
    void g() { std::cout << "S3::g\n"; }
};

template <class T, class = void>
struct D_f {};

template <class T>
struct D_f<T, decltype(void(&T::f))> : private virtual T {
    using T::f;
};

template <class T, class = void>
struct D_g {};

template <class T>
struct D_g<T, decltype(void(&T::g))> : private virtual T {
    using T::g;
};

template <class T>
struct D : D_f<T>, D_g<T> {
};


int main() {
    D<S1>().f();
    D<S2>().f();
    D<S2>().g();
    D<S3>().g();
    return 0;
}

Live Demo

【讨论】:

  • 不过,这不能很好地扩展。对于 n 个可能可用也可能不可用的函数,你需要拼出 2**n 类定义。
  • @BryanChen 是否可以限制主 D 类模板内部的更改,即不添加像 using T::f; 这样的专门化和重复代码?
  • 我有完全相同的问题,只是我的结构不是从T 继承的,T 只是一个模板变量。 @BryanChen 一旦我从您的解决方案中删除继承,它就不再起作用了。您知道原因吗?可以采取哪些措施来避免这种情况?
猜你喜欢
  • 1970-01-01
  • 2021-02-18
  • 1970-01-01
  • 1970-01-01
  • 2017-06-02
  • 2018-06-02
  • 2019-04-02
  • 2016-02-13
  • 2020-08-04
相关资源
最近更新 更多