如何在定义时静态检查模板化类?

How do I statically check my templated class at definition time?

本文关键字:检查 静态 定义      更新时间:2023-10-16

在C#或Java中,以下内容无法编译,因为我"忘记"了类声明中的where部分,它指定T是定义add方法的实例。

class C<T> {
T make(T t) {
return t.add(t);
}
}

如果我为模板参数指定不完整的requires,我想在C++中获得类似的编译时检查。

template <typename T>
requires true
class C {
public:
T make() {
return T{};
}
};

我想得到上面C++代码的编译错误,指出方法c.make依赖于默认可构造T,但是在Trequires约束中没有捕获。

我可以让编译器检查我的requires约束集是否足以涵盖类实现所做的一切吗?

我为此使用 gcc (GCC( 10.0.0 20191207(实验性(。

我想要的叫做定义检查,显然目前不可能

C++2a概念(以前称为"概念精简版"和/或概念TS(众所周知不支持"定义检查"。定义检查的想法是程序员可能会编写 [...]

https://quuxplusone.github.io/blog/2019/07/22/definition-checking-with-if-constexpr/

8.2 定义检查

概念当前不会阻止模板使用要求中未指定的操作。考虑:

template<Number N>
void algo(vector<N>& v){
for (auto& x : v) x%=2;
}
我们的 Number 概念不需要 %=,

所以 algo 的调用是否成功不仅取决于概念检查的内容,还取决于参数类型的实际属性:参数类型是否有 %=?如果没有,我们会收到延迟(实例化时间(错误。

有些人认为这是一个严重的错误。我不 [...]

http://www.w.stroustrup.com/good_concepts.pdf

当前的提案检查接口,这是用户的主要好处所在,但不是模板定义。这一点从一开始就是明确的。

https://isocpp.org/blog/2016/02/a-bit-of-background-for-concepts-and-cpp17-bjarne-stroustrup

template <class T>
requires std::is_default_constructible_v<T>
class C
{
static_assert(std::is_default_constructible_v<T>, 
"T is not default-constructible");
};
struct valid
{
};
class invalid
{
invalid() = delete;
};
int main()
{
C<valid>();
// C<invalid>(); // assertion fails.
}

您可以在类定义中的任何位置编写static_assert,以及requires.这将为您提供所需的错误消息。


更新阅读您提供的链接后,我想您只需要多次检查。

您可以编写特征结构:


// SFINAE to check if has "add"
template <class T, class = std::void_t<>>
struct has_method_add
{
constexpr static bool value = false;
};
template <class T>
struct has_method_add<T, std::void_t<decltype(&T::add)>>
{
constexpr static bool value = true;
};
template <class T, class = std::void_t<>>
struct has_operator_remainder
{
constexpr static bool value = false;
};
template <class T>
struct has_operator_remainder<T, std::void_t<decltype(&T::operator%=)>>
{
constexpr static bool value = true;
};
template <class T>
struct error_missing_add
{
constexpr static bool value = has_method_add<T>::value;
static_assert(has_method_add<T>::value, "T::add is not defined");
};
template <class T>
struct error_missing_remainder
{
constexpr static bool value = has_operator_remainder<T>::value;
static_assert(has_operator_remainder<T>::value, "T::operator%= is not defined");
};
template <class T>
class C
{
static_assert(std::conjunction_v<error_missing_add<T>, error_missing_remainder<T>>);
// impl...
};
struct valid
{
void add();
int operator%=(int) const;
};
struct missing_add
{
int operator%=(int) const;
};
struct missing_remainder
{
void add();
};
int main()
{
C<valid>{};
C<missing_add>{}; // error: T::add is not defined
C<missing_remainder>{}; // error: T::operator%= is not defined
return 0;
}