【发布时间】:2018-02-19 23:15:37
【问题描述】:
我正在尝试使用函数模板(或其他一些模板化构造)来模拟一个函数,而无需将隐式转换应用于参数或返回表达式。最可靠的方法是什么?
理想情况下,我试图想出一种方法来做到这一点,该方法很容易隐藏在模板或宏后面,有不错的错误消息,并且不会造成运行时损失。
这是一个元编程练习;我不会在生产代码中这样做。
下面的函数func1 将两个longs 相加,照常执行转换。
long func1(long a, long b) {
return a + b;
}
我想以这样一种方式定义func,即a 和b 只能是longs,并在编译时检查return 表达式确实具有指定的类型而没有正在插入隐式转换。
为了具体起见,到目前为止,我已经尝试了几种方法,它们就在这里。
防止转换(仅用于参数)的一种方法是使用具有已删除实现和参数组合的特化的函数模板。
template <class L1, class L2>
long func2(L1 a, L2 b) = delete;
template<>
long func2(long a, long b) {
return a + b;
}
也可以使用enable_if 来完成同样的事情。
template <class L1, class L2>
std::enable_if_t<
std::is_same<L1, long>::value && std::is_same<L2, long>::value
, long> func3(L1 a, L2 b) {
return a + b;
}
但是,我无法弄清楚如何在不花费自己副本或移动成本的情况下合并有关返回类型的断言,或者需要做复杂的事情来去除 CV 资格和参考性。
// doesn't work, unnecessary copy
template <class L1, class L2>
std::enable_if_t<
std::is_same<L1,long>::value && std::is_same<L2,long>::value,
long> func4(L1 a, L2 b) {
auto out = a + b;
static_assert(std::is_same<decltype(out), long>::value);
return out;
}
但是,我可以将函数体移动到 lambda 中,然后使用 static_asserts 列出我想要的所有条件。我有点担心这种方法会出人意料。
template <class L1, class L2>
long func5(L1 a, L2 b) {
static auto wrapped = [&](){
return a + b;
};
static_assert(std::is_same<decltype(a), long>::value);
static_assert(std::is_same<decltype(b), long>::value);
static_assert(std::is_same<decltype(wrapped()), long>::value);
return wrapped();
}
【问题讨论】:
-
您可能希望
-> decltype(auto)避免在您的检查中丢失参考。 -
你想对 const-volatile 限定符做什么?允许还是禁止?
-
@JiveDadson 那是……在我的问题中实际上是一个非常严重的疏忽。我想我想禁止它。通常我不会费心将
const添加到按值参数中,我也没有想到。 -
您的问题不清楚。尤其是您以
long为例,对于该示例而言,复制毫无意义。由于 RVO,您标记为“不必要的副本”的示例没有不必要的副本。反正RVO是先去掉cv-qualifier来决定的,不知道你为什么提到那些qualifier。 -
@liliscent 对不起,我在这个例子中使用了
long,因为它是我能想到的最典型的例子类型,它有一个你有时不想要的隐式转换。我打算使用各种策略来创建一个非转换伪函数以应用于任意函数(并且可能隐藏在宏后面),因此重要的是我不要对底层类型做出假设。
标签: c++ template-meta-programming