在不使用继承或 class 特化的情况下禁用成员函数和变量

Disable member functions and variables without using inheritance or class specialization

我有一个模板 class Foo 有两种变体:一种有一个额外的模板类型参数。我希望 class 在存在该参数时具有一些附加功能,即在 class 中存储一个成员变量,并为其提供一些 getter 和 setter。我还需要对附加类型执行额外的 static_assert(),我还需要禁用常规构造函数并提供一个也具有该类型参数的构造函数。它看起来像这样:

template <class Irrelevant, class Extra>
class Foo {
    //other stuff...
    static_assert(is_good_v<Extra>);
    Extra extra;
public:
    //other stuff...
    Foo(Irrelevant i) {/* irrelevant */}

    //now methods I need to enable only when Extra is present
    Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */}
    void setExtra(Extra e) {extra = e;}
    void getExtra() {return extra;}
};

我尝试了几种方法,但似乎都不满意。首先我尝试使用继承:

template <class Irrelevant>
class Bar {
    //other stuff...
public:
    //other stuff...
    Foo(Irrelevant i) {/* irrelevant */}
};

template <class Irrelevant, class Extra>
class Foo : public Bar<Irrelevant> {
    static_assert(is_good_v<Extra>);
    Extra extra;
public:
    Foo(Irrelevant i, Extra e) : Bar<Irrelevant>(i), extra(e) {/* irrelevant */}
    void setExtra(Extra e) {extra = e;}
    void getExtra() {return extra;}
};

template <class Irrelevant>
class Foo : public Bar<Irrelevant> {};

这(或类似的东西,至少)单独工作,但在其他地方对它们进行模板检查时需要额外的解决方法,而且它不必要地乱扔全局名称空间,我不应该这样做。 第二种方法是对 Foo 进行特化,如下所示:

template <class Irrelevant, class... T>
class Foo {};

template <class Irrelevant, class Extra>
class Foo<Irrelevant, Extra> {
    //other stuff...
    static_assert(is_good_v<Extra>);
    Extra extra;
public:
    //other stuff...
    Foo(Irrelevant i) {/* irrelevant */}

    //now methods I need to enable only when Extra is present
    Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */}
    void setExtra(Extra e) {extra = e;}
    void getExtra() {return extra;}
};


template <class Irrelevant>
class Foo<Irrelevant> {
    //other stuff...
public:
    //other stuff...
    Foo(Irrelevant i) {/* irrelevant */}
};

这也有效,并没有乱扔全局命名空间,但所有 //other stuff 都是重复的,这不是很好。我想尝试的第三种方法是 std::enable_if 所有这些东西,但我似乎无法让它发挥作用。我试过这样的事情:

template <class Irrelevant, class Extra = void>
class Foo {
    //other stuff...
    static constexpr bool hasExtra = std::is_same_v<Extra, void>;
    static_assert(!hasExtra || is_good_v<Extra>);
    std::conditional<hasExtra, Extra, bool> extra;
public:
    //other stuff...
    template <typename = typename std::enable_if_t<!hasExtra>>
    Foo(Irrelevant i) {/* irrelevant */}

    //now methods I need to enable only when Extra is present
    template <typename = typename std::enable_if_t<hasExtra>>
    Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */}

    template <typename = typename std::enable_if_t<hasExtra>>
    void setExtra(Extra e) {extra = e;}

    template <typename = typename std::enable_if_t<hasExtra>>
    void getExtra() {return extra;}
};

但这显然不起作用,因为它会产生编译错误,因为 SFINAE 在编译 getter 和 setter 时没有其他函数可以进入 SFINAE。并为 when Extra == void 添加虚拟函数将使有人使用这些函数的代码编译。我怎样才能正确地做到这一点?

如果 Foo 仅具有 extra 并且继承自部分专业化,比如 Foo<Irrelevant, void>,公共部分呢?

我的意思是:类似

// with extra only
template <typename Irrelevant, typename Extra>
class Foo : public Foo<Irrelevant, void>
 {
   private:
      Extra extra;

      static_assert(is_good_v<Extra>);

   public:
      // to make visible the inherited constructor(s)
      using Foo<Irrelevant, void>::Foo;

      // to disable (?) a specific inherited constructor
      // Foo(Irrelevant i) = delete;

      //now methods I need to enable only when Extra is present
      Foo(Irrelevant i, Extra e) : extra(e) {/* irrelevant */}

      void setExtra(Extra e) {extra = e;}

      Extra getExtra() {return extra;}
 };

// common part, no extra
template <typename Irrelevant>
class Foo<Irrelevant, void>
 {
   public:
      Foo(Irrelevant i) {/* irrelevant */}
 };