提问人:yonutix 提问时间:9/26/2023 最后编辑:Jan Schultkeyonutix 更新时间:9/26/2023 访问量:183
如何仅在提供模板参数时启用类模板成员函数?
How can you enable a class template member function only if a template argument was provided?
问:
是否可以有一个带有可选模板参数的类,可以像这样调用?
#include <iostream>
template <typename T = void>
class A final
{
public:
// This class can be called only when T exists.
void f()
{
printf("%d\n", member);
}
// This method can be called only when T is missing.
void g()
{
printf("No template parameter\n");
}
public:
T member;
};
int main()
{
A<int> a1;
A a2;
a1.f(); // should be valid
a1.g(); // should be invalid, cannot compile
a2.f(); // should be invalid, cannot compile
a2.g(); // should be valid
return 0;
}
如果是,应该使用哪些标准函数?
答:
7赞
dfrib
9/26/2023
#1
成员函数 API 的约束
您可以利用约束和概念,尤其是 std::same_as
概念:
#include <concepts>
template <typename T = void>
struct A final {
// This method is defined only when T is non-void.
void f() requires (!std::same_as<T, void>) {}
// This method is defined only when T is void.
void g() requires std::same_as<T, void> {}
};
int main() {
A<int> a1;
A a2;
a1.f(); // OK
// a1.g(); // error
// a2.f(); // error
a2.g(); // OK
}
会员呢?
如果您只希望该成员存在于某些专业化中,则可以将其包装在相应地专门化的类模板中。例如:
template<typename T>
struct Value {
T value;
};
template<>
struct Value<void> {};
另一种方法是使用帮助程序基类来继承成员,这在 A 的构造函数依赖于类型本身的特征时尤其有用。F.D.(英语:F.D.) 通常使用各种基类的 mixin 实现。std::optional
您可能还希望对类的模板参数本身进行限制,例如在类中包装哪种类型是有意义的。A
使用最小方法:
#include <concepts>
template<typename T>
struct Value {
T value;
};
template<>
struct Value<void> {};
template <typename T = void>
struct A final {
// This methods can be called only when T is non-void.
T f() requires (!std::same_as<T, void>) {
return member.value;
}
// This method can be called only when T is void.
void g() requires std::same_as<T, void> {}
private:
Value<T> member;
};
int main() {
A<int> a1;
A a2;
auto value = a1.f(); // OK
// a1.g(); // error
// a2.f(); // error
a2.g(); // OK
}
评论
0赞
aschepler
9/26/2023
虽然这太糟糕了,但据我所知,只有当是时才没有办法给出属性。我想如果需要的话,私人继承黑客可能会有所帮助。member
[[no_unique_address]]
T
void
0赞
Daniel Langr
9/26/2023
@aschepler 它会有什么帮助吗?无论如何,一个对象都会占用(至少)1 个字节,即使 的存储被优化了。A<void>
member
0赞
Caleth
9/26/2023
如果它是其他类的成员,则@DanielLangr不行[[no_unique_address]]
0赞
Jarod42
9/26/2023
@DanielLangr:如果包含其他成员,会有所帮助。A
0赞
Daniel Langr
9/26/2023
@Jarod42我同意。但是,EBO和在这方面有什么区别吗,正如aschepler所假设的那样?AFAIK,此属性甚至可以用于非空类型的成员变量。[[no_unique_address]]
8赞
Jarod42
9/26/2023
#2
您可以将“旧”方式用于专业化:
template <typename T = void>
class A final // generic case, T != void
{
public:
void f() { std::cout << member << std::endl; }
public:
T member;
};
template <>
class A<void> final
{
public:
void g() { printf("No template parameter\n"); } // actually `void` and not "No".
};
通过将类转换为可变参数,将 absent 参数作为空包而不是 void 进行处理,您可以执行以下操作:
template <typename... Ts>
class A final
{
static_assert(sizeof...(Ts) < 2);
public:
void f() requires (sizeof...(Ts) == 1) { std::cout << std::get<0>(member) << std::endl; }
void g() requires (sizeof...(Ts) == 0) { printf("No template parameter\n"); }
public:
std::tuple<Ts...> member;
};
评论
3赞
NathanOliver
9/26/2023
我喜欢元组方法,它对我来说非常开箱即用。它还提供了相同的尺寸,因此没有额外的膨胀。
评论
A<void>
T
void
std::enable_if
std::void_t
std::enable_if