2016-07-05 10 views
7

header for optionally-lazy parametersGitHub repositoryにも表示されています)を作成しました。 (これはnot my first question based on the headerです)仮想デストラクタはdecltypeの動作を変更します

私は基本クラスのテンプレートと2つの派生クラスのテンプレートを持っています。基底クラスのテンプレートはstatic_assertprotectedコンストラクタを持ちます。このコンストラクタは、特定の派生クラスによってのみ呼び出されます。 static_assertの中に私はdecltypeを使用しています。

本当に奇妙な事がdecltype内部名のタイプは何とか私の基本クラステンプレートの仮想デストラクタがあるかどうかによって影響されることです。デストラクタがコメントアウトされている場合、これはエラーなしでコンパイルされることを

#include <type_traits> 
#include <utility> 

template <typename T> 
class Base 
{ 
    protected: 
    template <typename U> 
    Base(U&& callable) 
    { 
     static_assert(
      std::is_same< 
       typename std::remove_reference<decltype(callable())>::type, T 
      >::value, 
      "Expression does not evaluate to correct type!"); 
    } 

    public: 
    virtual ~Base(void) =default; // Causes error 

    virtual operator T(void) =0; 
}; 

template <typename T, typename U> 
class Derived : public Base<T> 
{ 
    public: 
    Derived(U&& callable) : Base<T>{std::forward<U>(callable)} {} 

    operator T(void) override final 
    { 
     return {}; 
    } 
}; 

void TakesWrappedInt(Base<int>&&) {} 

template <typename U> 
auto MakeLazyInt(U&& callable) 
{ 
    return Derived< 
      typename std::remove_reference<decltype(callable())>::type, U>{ 
     std::forward<U>(callable)}; 
} 

int main() 
{ 
    TakesWrappedInt(MakeLazyInt([&](){return 3;})); 
} 

注:

は、ここに私のMCVEです。

callableはの式になり、()演算子で呼び出されたときにはTの何かが返されます。 Baseに仮想デストラクタがなければ、これは正しく評価されているようです。 の仮想デストラクタを使用すると、callabeleのタイプはBase<T>であることがわかります(私の言う限り、意味をなさない)。

recursive_lazy.cpp:13:55: error: type 'Base<int>' does not provide a call operator 
       typename std::remove_reference<decltype(callable())>::type, T 
                 ^~~~~~~~ 
recursive_lazy.cpp:25:7: note: in instantiation of function template specialization 
     'Base<int>::Base<Base<int> >' requested here 
class Derived : public Base<T> 
    ^
1 error generated. 

Here is an online version.

EDIT:=deleteコピーコンストラクタを-ing

recursive_lazy.cpp: In instantiation of ‘Base<T>::Base(U&&) [with U = Base<int>; T = int]’: 
recursive_lazy.cpp:25:7: required from ‘auto MakeLazyInt(U&&) [with U = main()::<lambda()>]’ 
recursive_lazy.cpp:48:47: required from here 
recursive_lazy.cpp:13:63: error: no match for call to ‘(Base<int>)()’ 
       typename std::remove_reference<decltype(callable())>::type, T 

はここクラン++ 3.7のエラーメッセージです:

は、ここでG ++ 5.1のエラーメッセージですもこのエラーを引き起こします。

+2

'r値をとる(U &&呼び出し可能)私は奇妙な仮想デストラクタエラーをご紹介できなかったが、私は'派生いることがわかりユニバーサルリファレンスではありません。それは意図されていますか? – SirGuy

+0

何をするべきかを示す例にいくつかの出力を追加できますか? TakesWrappedIntは、最後の演算子T();のためにあなたの例でゼロになるようです。 –

+0

@GuyGreerいいえ、それは意図されていません。これは、いったんテンプレートが特殊化されると、Uはもはやテンプレートタイプではないからですか? –

答えて

10

問題が

(N4594 12.8/9)

ので、クラスXの定義は明示的に宣言しない場合、あなたはデストラクタを宣言するときに、暗黙の移動コンストラクタが宣言されないことですデフォルト設定として移動コンストラクタは、非明示的な一つは、暗黙的に に宣言される...場合にのみ

  • Xは、ユーザが宣言したデストラクタ

Baseは(不履行だとは関係ありません)、ユーザー宣言デストラクタを持つ必要はありません。

MakeLazyIntDerivedオブジェクトを返そうとすると、Derived移動コンストラクタが呼び出されます。

Derived暗黙的に宣言された移動コンストラクタは、Base移動コンストラクタ(存在しないため)を呼び出すのではなく、テンプレートのBase(U&&)コンストラクタを呼び出します。ここ

とは問題だ、callableパラメータが本当にoperator()を含まない、呼び出し可能オブジェクトが、Baseオブジェクトが含まれていません。単にBase内を移動コンストラクタを宣言し、問題を解決するために

template <typename T> 
class Base 
{ 
    protected: 
    template <typename U> 
    Base(U&& callable) 
    { 
     static_assert(
      std::is_same< 
       typename std::remove_reference<decltype(callable())>::type, T 
      >::value, 
      "Expression does not evaluate to correct type!"); 
    } 

    public: 
    virtual ~Base(void) =default; // When declared, no implicitly-declared move constructor is created 

    Base(Base&&){} //so we defined it ourselves 

    virtual operator T(void) =0; 
}; 
+1

...また、コピーの削除に関するOPの更新についても説明していますコンストラクタ... – davidbak

+0

Ahhhhh。私はそのルールを知っていますが、エラーメッセージに基づいて、どのように適用されたのか分かりませんでした。ありがとうございました。 C++ 17で保証されているコピー抹消がこの問題を回避できたら知っていますか? –

+0

@KyleStrandよく、第2のルールに基づいています*関数呼び出しでは、return文のオペランドがprvalueで、関数の戻り値の型がそのprvalue。*の型と同じ場合、コンパイラはmoveコンストラクタを削除しますしかし、なぜコンパイラがこのケースでそれを省略しなかったのか分かりません。 – PcAF

関連する問題