类模板的成员函数有条件无效(隐式实例化有效;显式实例化失败)
Conditionally invalid member function of class template (implicit instantiation works; explicit instantiation fails)
我创建了一个类模板。根据其模板参数,它支持不同的操作。类模板应(隐式)实例化为模板参数的多个组合。对于其中一些组合,可能存在没有意义(并且也无法编译)的成员函数。但是,只要我不强制执行显式实例化,一切似乎都会按预期工作。
现在,有这些可怕的案例被称为"未指定"、"未定义"、"格式不良;无需诊断", ....我绝对想避免这些事情中的任何一个。因此,我正在征求如何处理这种情况的建议。
下面是一个显示相同观察结果的示例。请注意,我对如何修复这个确切的玩具示例不太感兴趣。
#include <iostream>
#include <type_traits>
template<class T>
struct SingleSink {
SingleSink(T) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<class T>
struct DoubleSink {
DoubleSink(T, T) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<class T, int arity /*, some other stuff */>
struct SuperSink {
// This class shall do something special depending on, say, `arity`.
// Instead of partially specializing the whole class template (and introducing
// code duplication for the remaining functionality), let us externalize the
// `arity`-dependent behavior to a special member.
using Sink = std::conditional_t<
arity == 1,
SingleSink<T>,
DoubleSink<T>
>;
Sink sink_;
// [some more data members that do not depend on `arity`]
// for a fixed `Sink` one of the following constructors should fail to compile
SuperSink(T i) : sink_{i} {}
SuperSink(T i, T j) : sink_{i, j} {}
// ... so these are what I call "conditionally invalid member functions".
};
// explicit instantiation yields error (deactivated by comments):
// template struct SuperSink<int, 1>;
// template struct SuperSink<int, 2>;
int main() {
// implicit instantiation works
SuperSink<int, 1>{5};
SuperSink<int, 2>{5, 6};
// these yield a compile error (as desired)
// SuperSink<int, 1>{5, 6};
// SuperSink<int, 2>{5};
}
- 如果我从不,这些条件无效的成员函数是问题吗 需要显式实例化?
- 如果是:检查显式实例化是否有效是好的做法吗?
如果我从不需要显式实例化,这些条件无效的成员函数会成为问题吗?
即使是来自 STL 的模板也有"无效"的方法,例如:std::vector<T>::resize(std::size_t)
具有非默认可构造T
。
因此,使用"无效"方法,您的类可以正常使用。记录您的要求是一种选择。
但是,这些方法对SFINAE并不友好,因为错误不会出现在直接上下文中,而是出现在属性中。
当无效时,您可以自行使用 SFINAE 将其删除,例如:
template <std::size_t N = arity, std::enable_if_t<N == 1, int> = 0>
SuperSink(T i) : sink_{i} {}
template <std::size_t N = arity, std::enable_if_t<N != 1, int> = 0>
SuperSink(T i, T j) : sink_{i, j} {}
在 C++2a 中,您可以指定一些条件以在类中包含方法(类似于上面的 SFINAE,但语法更好,没有额外的模板):
SuperSink(T i) requires (arity == 1) : sink_{i} {}
SuperSink(T i, T j) requires (arity != 1) : sink_{i, j} {}
我至少看到了一个问题,即设计破坏了如下所示的类型特征,因此当使用 SFINAE 来决定调用哪些构造函数时,您可能会遇到问题。
static_assert(!std::is_constructible<SuperSink<int, 1>, int, int>::value);
static_assert(!std::is_constructible<SuperSink<int, 2>, int>::value);
您可以通过反转设计来解决此问题:将SingleSink
和DoubleSink
定义为通用SuperSink
的特殊情况(或如果需要:专业化)。
#include <iostream>
#include <type_traits>
template<class T, int arity>
struct SuperSink {
template<typename... Ts, typename = std::enable_if_t<sizeof...(Ts) == arity> >
SuperSink(Ts... is) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<typename T>
struct SingleSink : SuperSink<T, 1> {
using Base = SuperSink<T, 1>;
using Base::Base; // inherit constructor
// implement special functionality here
};
template<typename T>
struct DoubleSink : SuperSink<T, 2> {
// follow same pattern as in SingleSink.
}
int main() {
// implicit instantiation works
SuperSink<int, 1>{5};
SuperSink<int, 2>{5, 6};
// Now, these work as desired
static_assert(!std::is_constructible<SuperSink<int, 1>, int, int>::value);
static_assert(!std::is_constructible<SuperSink<int, 2>, int>::value);
// these yield a compile error (as desired)
// SuperSink<int, 1>{5, 6};
// SuperSink<int, 2>{5};
}
相关文章:
- 从C++实例化QML
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何创建一个空的全局类并在启动时实例化它
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 约束和显式模板实例化
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 对象实例化调用构造函数的次数太多
- 如何使用非默认构造函数实例化模板化类
- 静态数据成员模板专用化的实例化点在哪里
- 错误的cv::face FacemarkLBF实例化
- C++的解析器在可以区分比较和模板实例化之前会做什么?
- 通过 typedef 模板<类型名 T、T> 强制模板实例化 - 为什么有效?
- 找不到使用 bitset 实例化模板函数的有效方法
- 是否实例化了所有有效的模板
- 类模板的成员函数有条件无效(隐式实例化有效;显式实例化失败)
- 模板中的方法:一个有效,一个不实例化
- int&& 在模板实例化中有效吗?
- c++编译器隐式实例化模板类的所有成员函数是否有效?
- 关于基类中的显式模板实例化和静态变量:编译器错误或规范的有效解释
- 显式模板实例化在XLC上产生编译错误,但在其他编译器上有效