【问题标题】:C++ how to emulate a function without implicit conversions?C ++如何在没有隐式转换的情况下模拟函数?
【发布时间】:2018-02-19 23:15:37
【问题描述】:

我正在尝试使用函数模板(或其他一些模板化构造)来模拟一个函数,而无需将隐式转换应用于参数或返回表达式。最可靠的方法是什么?

理想情况下,我试图想出一种方法来做到这一点,该方法很容易隐藏在模板或宏后面,有不错的错误消息,并且不会造成运行时损失。

这是一个元编程练习;我不会在生产代码中这样做。

下面的函数func1 将两个longs 相加,照常执行转换。

long func1(long a, long b) {
  return a + b;
}

我想以这样一种方式定义func,即ab 只能是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();
}

【问题讨论】:

  • 您可能希望-&gt; decltype(auto) 避免在您的检查中丢失参考。
  • 你想对 const-volatile 限定符做什么?允许还是禁止?
  • @JiveDadson 那是……在我的问题中实际上是一个非常严重的疏忽。我想我想禁止它。通常我不会费心将const添加到按值参数中,我也没有想到。
  • 您的问题不清楚。尤其是您以long 为例,对于该示例而言,复制毫无意义。由于 RVO,您标记为“不必要的副本”的示例没有不必要的副本。反正RVO是先去掉cv-qualifier来决定的,不知道你为什么提到那些qualifier。
  • @liliscent 对不起,我在这个例子中使用了long,因为它是我能想到的最典型的例子类型,它有一个你有时不想要的隐式转换。我打算使用各种策略来创建一个非转换伪函数以应用于任意函数(并且可能隐藏在宏后面),因此重要的是我不要对底层类型做出假设。

标签: c++ template-meta-programming


【解决方案1】:

可能有很多方法可以做到这一点。这是一个。我认为没有任何理由检查所有这些情况。你想对 const-volatile 限定符做什么?允许还是禁止?

#include <type_traits>
#include <iostream>

template<typename Long>
Long func1(Long a, Long b) {
    static_assert(std::is_same_v<Long, long>);
    return a + b;
}
int main()
{
    long a = 1;
    const long b = 2;
    const long &c = a;
    std::cout << func1(c,b) << std::endl;
}

【讨论】:

  • 感谢您的勾选。还在为那个烤箱存钱。
  • 如何确保参数或返回值不被转换?
  • @Walter - 你的意思是func1(1L, static_cast&lt;long&gt;(double_var))?它没有,它不能,也不应该。我不知道 OP 为什么要做出限制,但是在 C/C++ 中,没有办法阻止一个坚持射脚的程序员。
  • 上例中的static_cast 是我想要强制人们执行的操作,因为它使转换变得明确。它(理论上)让用户/审阅者/任何人考虑转换是否是正确的做法。
  • func1(long(0),1) 怎么样?这会编译吗?在long的情况下,内置的operator+返回long,但一般情况下,不能保证相同类型相加的结果再次得到相同类型。因此,在您的func1 中,a+b 的结果可能与Long 不同。
【解决方案2】:

不需要 lambda:

template <class L1, class L2>
long func2(L1, L2) = delete;

decltype(auto) func2(long a, long b) {
  return a + b;
}

static_assert(std::is_same<long, decltype(func2(42L, std::declval<long>()))>::value);

【讨论】:

    【解决方案3】:

    无需生成临时来测试其类型,只需使用decltype

    template <class L1, class L2>
    auto func3(L1 a, L2 b)
    -> std::enable_if_t<
         std::is_same<L1, long>::value &&
         std::is_same<L2, long>::value &&
         std::is_same<decltype(a+b), long>::value, 
         long>
    {
      return a + b;
    }
    

    static_assert:

    template<typename T1, typename T2>
    void func(T1, T2) = delete;
    
    long func(long a, long b)
    {
      static_assert(std::is_same<long,decltype(a+b)>::value,"!!");
      return a+b;
    }
    

    【讨论】:

      猜你喜欢
      • 2015-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-28
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 2021-05-05
      相关资源
      最近更新 更多