我已经实现了一个功能,该功能基于在GotW#23中显示并在#28中引用的新的示例的普通平原错误头脑定位示例的想法来执行“就地对象回收” hack。

(是的,是的,我知道...但是在作为处理大量消息的应用程序中,作为一种非常引人注目的优化,该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 数转换为浮点数,而不会发出clang++甚至警告的信息。

这使我想知道我是否对explicit关键字在构造函数上所做的事情是否有错。

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

基本上,我可能需要的是get_constructor_args<T>之类的内容,以便将另一个enable_if放入is_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

您可以根据需要删除该运行时检查。只需消除最后两个Recyles。 variardic将处理移动分配等。

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

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

10-10 15:37