一种安全,符合标准的方法,只有在实例化了static_assert的情况下,才能使类模板专业化而无法编译?

Qui*_*mby 8 c++ templates static-assert language-lawyer

假设我们要创建一个只能用数字实例化的模板类,否则就不应该编译。我的尝试:

#include <type_traits>

template<typename T, typename = void>
struct OnlyNumbers{
public:
    struct C{};
    static_assert(std::is_same<C,T>::value, "T is not arithmetic type.");

    //OnlyNumbers<C>* ptr;
};

template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>{};

struct Foo{};
int main()
{
    OnlyNumbers<int>{}; //Compiles
    //OnlyNumbers<Foo>{}; //Error
}
Run Code Online (Sandbox Code Playgroud)

现场演示 -所有三个主要的编译器似乎都能按预期工作。我知道已经有一个类似的问题,答案引用了该标准。接受的答案使用temp.res.8temp.dep.1来回答该问题。我认为我的问题有所不同,因为我只是在问我的示例,我不确定标准对此的看法。

我认为我的程序不是格式错误的,并且仅当编译器尝试实例化基本模板时,它才应该无法编译。我的推理:

  • [temp.dep.1]:

    在模板内部,某些构造的语义可能因一个实例而异。这样的构造取决于模板参数。

    这应该使std::is_same<C,T>::value依赖T

  • [temp.res.8.1]:

    如果模板中的语句或模板未实例化,则无法为模板或constexpr的子语句生成有效的专业化名称;或者

    不适用,因为存在有效的专业化,尤其OnlyNumbers<C>是有效的,并且可以在类内部用于例如定义成员指针variable(ptr)。实际上,通过删除断言并取消注释ptrOnlyNumbers<Foo>代码行得以编译。

  • [temp.res.8.2-8.4]不适用。

  • [temp.res.8.5]我也不认为这适用,但是我不能说我完全理解本节。

我的问题是:我的推理正确吗?这是一种使特定[class] *模板static_assert仅在实例化时无法编译的安全,符合标准的方法吗?

*基本上,我对类模板感兴趣,请随时包含函数模板。但是我认为规则是相同的。

**这意味着没有TT=C从内部使用那样可以从外部实例化模板。即使C可以通过某种方式访问​​,我也不认为有一种方法可以引用它,因为它会导致这种递归OnlyNumbers<OnlyNumbers<...>::C>

编辑:

为了清楚起见,我知道我可以构造一个表达式,如果其他任何专业都不匹配,则该表达式将完全为假。但这很快就会变得冗长,并且如果专业变更,则容易出错。

Han*_*999 3

静态断言可以直接在类中使用,而不需要做任何复杂的事情。

#include <type_traits>

template<typename T>
struct OnlyNumbers {
    static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
    // ....
};
Run Code Online (Sandbox Code Playgroud)

在某些情况下,您可能会收到其他错误消息,因为实例化非算术类型的 OnlyNumbers 可能会导致更多编译错误。

我不时使用的一个技巧是

#include <type_traits>

template<typename T>
struct OnlyNumbers {
    static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
    using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
    // ....
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您的类将使用有效类型 int 进行实例化。由于静态断言无论如何都会失败,因此这不会产生负面影响。