c++ - 防止转发为 "too liberal"

标签 c++ c++11 variadic-templates enable-if

我已经根据 GotW 中显示的可悲的简单错误按放置新分配示例的想法实现了执行“就地对象回收”黑客攻击的功能#23 并在#28 中提到。

(是的,是的,我知道......但是这个 hack 实际上已经显示出非常有用,作为一个处理大量消息的应用程序中非常明显的优化,我想如果使用得当它是足够安全的。)

代码在 clang++ 3.5 上编译时没有警告并且“工作正常”,但在我看来它有点过于自由,因为它允许一些可能意外发生并且可能不受欢迎的隐式转换:

#include <type_traits>

template<typename T>
typename std::enable_if<std::is_nothrow_move_constructible<T>::value, T&>::type
recycle(T& obj, T&& other)  noexcept
{ obj.~T(); new (&obj) T(other);  return obj; }

template<typename T, typename... A>
typename std::enable_if<std::is_nothrow_constructible<T>::value && std::is_nothrow_move_constructible<T>::value, T&>::type
recycle(T& obj, A&&... arg) noexcept
{ obj.~T(); new (&obj) T(arg...); return obj; }


int main()
{
    struct foo
    {
        foo() noexcept {}
        explicit foo(float) noexcept {}
    };

    foo b;
    recycle(b, foo());     // OK, calls #1, move-constructing from default temporary
    recycle(b, foo(1.0f)); // OK, as above, but non-default temporary
    recycle(b, 5.0f);      // OK, calls #2, forwarding and move-constructing
    recycle(b, 5.0);       // succeeds calling #2 (undesired implicit double --> float conversion)
    recycle(b, 5);         // succeeds calling #2 (undesired implicit int ---> float conversion)
    recycle(b, b);         // succeeds, but should probably fail a runtime check (undefined behavior)

    return 0;
}

有些调用在您不期望它们时编译得很好,这可能是因为模板参数包捕获了“所有内容”,但我仍然很惊讶自 foo 以来它如何一起工作的构造函数是 explicit ,无论哪种方式,模板都必须调用它。我不确定它如何将整数和 double 转换为 float 而不发出 clang++ 甚至发出警告。

这让我想知道我是否对 explicit 的假设有误关键字在构造函数上执行。

自赋值(将调用未定义的行为)对我来说不是问题,因为尝试将对象与自身一起回收首先显示出一些严重的大脑故障(因此我决定不添加运行时检查那个),但是意外地调用一个带有隐式可转换参数的函数是很容易发生的事情,如果可能的话,我想捕获编译器错误。

基本上我可能需要的是 get_constructor_args<T> 之类的东西放入另一个 enable_ifis_convertible .还是什么……概念?

有没有办法用 C++11(或 C++14,如果你愿意的话)实现这种事情?

最佳答案

template<class T, class...Args>
using safe_to_recycle = std::integral_constant<bool,
  std::is_nothrow_constructible<T, Args...>::value
  && std::is_nothrow_destroyable<T>::value
>;


template<typename T, typename... Args>
std::enable_if_t<safe_to_recycle<T,Args...>::value, T&>
recycle(T& obj, Args&&... args)  noexcept
{
  obj.~T();
  new (&obj) T{std::forward<Args>(args)...};
  return obj;
}

template<typename T, typename... Args>
std::enable_if_t<safe_to_recycle<T,Args...>::value, T&>
recycle_explicit(T& obj, Args&&... args)  noexcept
{
  obj.~T();
  new (&obj) T(std::forward<Args>(args)...);
  return obj;
}

template<typename T, typename U>
std::enable_if_t<
  std::is_same<std::decay_t<T>,std::decay_t<U>>::value &&
  safe_to_recycle<T,U>::value,
  T&
>
recycle(T& obj, U&& rhs)  noexcept
{
  if (&obj == &rhs) return obj;
  obj.~T();
  new (&obj) T{std::forward<U>(rhs)};
  return obj;
}

template<typename T, typename U>
std::enable_if_t<
  std::is_same<std::decay_t<T>,std::decay_t<U>>::value &&
  safe_to_recycle<T,U>::value,
  T&
>
recycle_explicit(T& obj, U&& rhs)  noexcept
{
  if (&obj == &rhs) return obj;
  obj.~T();
  new (&obj) T(std::forward<U>(rhs));
  return obj;
}

如果您缺少 C++14,请编写您自己的 enable_if_tdecay_t

如果需要,您可以放弃运行时检查。只需消除最后 2 次回收。 variardic 将处理移动分配等。

请注意,显式构造函数不会被 recycle 调用——您必须 recycle( obj, T(5.0f) )recycle_explicit 将进行缩小转换。

关于c++ - 防止转发为 "too liberal",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27427673/

相关文章:

c++ - 是否可以对参数包进行类型定义?

c++ - 无法理解为什么完美转发不起作用

C++11 通过引用定义函数类型捕获

c++ - 在不使用继承或类特化的情况下禁用成员函数和变量

c++ - 为什么这个函数调用没有拒绝不合适的重载?

C++ 字符串 char** 翻译

c++ - 使用 switch 将名称转换为常量,无需使用丑陋的代码

c++ - C++ 虚拟继承是如何在编译器中实现的?

c++ - 可变模板类 : support void without specializing the whole class?

c++ - 如何制作递归编译时可变结构模板