仅当提供了模板参数时,如何才能启用类模板成员函数?

yon*_*tix 6 c++ templates member-functions c++-concepts c++20

是否可以有一个带有可选模板参数的类,可以像这样调用?:

#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;
}
Run Code Online (Sandbox Code Playgroud)

如果是,应该使用哪些 std 函数?

Jar*_*d42 8

您可以使用“旧”方式进行专业化:

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".
};
Run Code Online (Sandbox Code Playgroud)

通过将你的类变成可变参数,将缺少的参数处理为空包,而不是 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;
};
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢元组方法,它对我来说非常开箱即用。它还提供相同的尺寸,因此不会额外膨胀。 (3认同)

dfr*_*fri 7

成员函数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
}
Run Code Online (Sandbox Code Playgroud)

那会员呢?

如果您只希望该成员针对某些专业化而存在,则可以将其包装在您相应专业化的类模板中。例如:

template<typename T>
struct Value {
    T value;
};

template<>
struct Value<void> {};
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用辅助基类来继承成员,这在 A 的构造函数依赖于类型本身的特征时尤其有用。Egstd::optional通常使用各种基类的 mixins 来实现。

您可能还想对类的模板参数本身进行限制,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
}
Run Code Online (Sandbox Code Playgroud)