【发布时间】:2017-11-28 17:43:11
【问题描述】:
我有一个模板类Foo,它有两种变体:一种有一个额外的模板类型参数。当该参数存在时,我希望该类具有一些附加功能,即在类中存储一个成员变量,并为其提供一些 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;}
};
但这显然不起作用,因为它会生成一个编译错误,因为在编译 getter 和 setter 时,SFINAE 没有其他函数可以让 SFINAE 进入。并为 Extra == void 添加虚拟函数将使有人使用这些函数的代码编译。我怎样才能正确地做到这一点?
【问题讨论】:
-
如果你需要 mixins 而不是使用CRTP。
-
我可以,但正如我所写,我不希望将除 Foo 之外的任何名称引入全局命名空间。
-
这如何迫使 mixin 类存在于全局命名空间中?只需根据需要添加另一层间接和
enable_if()。
标签: c++ c++17 variadic-templates sfinae template-specialization