2014-12-11 2 views
1

단순한 평범함의 환상적인 아이디어를 기반으로 "현재 위치에서 개체 재활용"해킹을 수행하는 기능을 구현했습니다 배정 별 배정 - 새 예 GotW # 23에 표시되고 # 28에서 참조된다.전달이 "자유주의"가되지 못하도록 함

(예, 예, 알고 있습니다. 그러나 실제로 많은 메시지를 처리하는 응용 프로그램에서이 해킹이 매우 눈에 띄는 최적화로 유용하게 사용되었습니다.

코드는 3.5 ++ 연타에 경고없이 컴파일하고 "잘 작동"하지만 accidentially 일어날 수 있으며 이는이 바람직하지 않을 수있는 몇 가지 암시 적 변환 할 수 있습니다로 다소 내 의견으로는 너무 진보적이다 :

#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; 
} 

을 사실을 템플릿 매개 변수 팩 catc가 필요하기 때문에 예상하지 못할 때 일부 컴파일은 올바르게 컴파일됩니다. hes "everything"이라고 말하지만, 나는 foo의 생성자가 explicit이고 템플릿이 그것을 호출해야하기 때문에 alltogether가 어떻게 작동하는지 아직도 놀랍다. 나는 clang ++없이 경고를 내뿜 지 않고 정수와 double을 어떻게 float으로 변환 할 수 있을지 확신하지 못한다.

이것은 내가 explicit 키워드가 생성자에 대해하는 일에 대해 뭔가 잘못되었다고 생각하는지 궁금하게 만듭니다.

자체 할당 (정의되지 않은 동작을 유발 함)은 처음에는 심각한 두뇌 실패를 보여주기 때문에 자체적으로 객체를 재활용하려고 시도한 이후로 나에게 별다른 문제가 아닙니다 (따라서 런타임 검사를 추가하지 않기로 결정했습니다. 그럴 경우), 암시 적으로 변환 가능한 매개 변수가있는 함수를 우연히 호출하는 것은 매우 쉽게 발생할 수있는 일이며 가능하다면 컴파일러 오류로 catch하고 싶습니다.

기본적으로 내가 필요한 것은 get_constructor_args<T>과 같은 것이고 enable_ifis_convertible입니다. 아니면 뭔가 ... 개념?

C++ 11 (또는 C++ 14)을 사용하여 이런 종류의 작업을 수행 할 수 있습니까?

+0

[좁은 변환을 잡으려면'{}'를 사용하십시오.] (http://coliru.stacked-crooked.com/a/ea5c188e4ab2b0b1). 또한,'other'가 lvalue (이름을 가지고 있기 때문에)의 첫 번째'recycle'은 항상 복사됩니다. 'std :: forward'를 사용하면 임시로 전달 될 때 이동하게됩니다. 마지막으로,'recycle (b, 5.0f);은 아무 것도 움직이지 않는다. –

+1

''recycle (T &, T &&)'는 최적화 대 이동 할당이라고 생각하기가 어렵습니다. 이동 할당이 끔찍한 경우가 아니면. – Casey

답변

1
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 개의 릴릴을 제거하면됩니다. 변수는 이동 할당 등을 처리합니다.

recycle에 의해 명시 적 생성자가 호출되지 않습니다. 사용자는 recycle(obj, T(5.0f))이어야합니다. recycle_explicit은 전환을 축소 할 것입니다.

+0

'recycle'과'recycle_explicit'의'T &, U &&'버전은 컴파일러 에러를 야기하는 불필요한 팩 확장을 가지고 있습니다 만, 이것을 제거한 후에는'enable_if_t'와'decay_t'가있는 한 정확하게 작동하는 것으로 보입니다. 고맙습니다. 이제 밤에 _how를 알아 내야하고 왜 정확히 작동하는지 :-) – Damon

+0

@Damon 때문에 편집 버튼이 있습니다. :) 기본적인 아이디어는 완벽한 포워딩입니다 ...음, 앞으로. 그래서 대부분의 경우를 처리합니다. 특별한 경우 코드는 one-arg 의사 복사 생성자를 탐지하고 포인터 테스트를 수행합니다. 언급했듯이, 아마도 그럴만 한 가치는 없습니다. 아마도 주장하고 계속해야합니다. 'safe_to_recycle'은 하나의 테스트에 두개의 테스트 (빌드와 destrow가 아닌)를 하나의 테스트로 포장하기 때문에 객체가 존재하지 않을 때 throw 할 수있는 방식으로'recycle '을 던질 수는 없습니다 (이중 파괴와 UB) – Yakk