ostreamでstd :: endlを使用する '<<'-バリアントメンバーを含む可変個引数テンプレートクラスでの演算子のオーバーロードにより、コンパイラエラーが発生する

バイトユニット

次のコードがあります。ここでは、を含み、VariantContainerの可変個引数テンプレート引数を使用する可変個<<引数テンプレート引数タイプのostream演算子オーバーロードを定義してVariantContainerいますstd::variantstd::variant

#include <iostream>
#include <variant>

template<typename... VARIANT_TYPES>
struct VariantContainer {
    std::variant<VARIANT_TYPES...> var;
};

template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst) {
    std::visit([&os](auto&& elem){os<<elem;}, inst.var);
    return os;
}

int main() {
    //If the following line is commented out, a compiler error is raised
    //std::cout << std::endl;
}

この例は、-std = c ++ 17(https://godbolt.org/z/5qvT3f)を使用してgcc-8、gcc-9、およびgcc-10でコンパイルされますしかし、std::cout-lineを有効にすると、次のコンパイラエラーが発生します。

In file included from <source>:2:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'constexpr const bool std::__detail::__variant::_Traits<>::_S_default_ctor':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1279:11:   required from 'class std::variant<>'
<source>:6:36:   required from 'struct VariantContainer<>'
<source>:17:23:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:286:4: error: invalid use of incomplete type 'struct std::__detail::__variant::_Nth_type<0>'
  286 |    is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:61:12: note: declaration of 'struct std::__detail::__variant::_Nth_type<0>'
   61 |     struct _Nth_type;
      |            ^~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'class std::variant<>':
<source>:6:36:   required from 'struct VariantContainer<>'
<source>:17:23:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1298:39: error: static assertion failed: variant must have at least one alternative
 1298 |       static_assert(sizeof...(_Types) > 0,
      |                     ~~~~~~~~~~~~~~~~~~^~~
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/move.h:57,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/nested_exception.h:40,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/exception:148,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ios:39,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1361:2:   required from 'class std::variant<>'
<source>:6:36:   required from 'struct VariantContainer<>'
<source>:17:23:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2554:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
 2554 |     using enable_if_t = typename enable_if<_Cond, _Tp>::type;
      |           ^~~~~~~~~~~
ASM generation compiler returned: 1
In file included from <source>:2:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'constexpr const bool std::__detail::__variant::_Traits<>::_S_default_ctor':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1279:11:   required from 'class std::variant<>'
<source>:6:36:   required from 'struct VariantContainer<>'
<source>:17:23:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:286:4: error: invalid use of incomplete type 'struct std::__detail::__variant::_Nth_type<0>'
  286 |    is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:61:12: note: declaration of 'struct std::__detail::__variant::_Nth_type<0>'
   61 |     struct _Nth_type;
      |            ^~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant: In instantiation of 'class std::variant<>':
<source>:6:36:   required from 'struct VariantContainer<>'
<source>:17:23:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1298:39: error: static assertion failed: variant must have at least one alternative
 1298 |       static_assert(sizeof...(_Types) > 0,
      |                     ~~~~~~~~~~~~~~~~~~^~~
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/move.h:57,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/bits/nested_exception.h:40,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/exception:148,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ios:39,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:38,
                 from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/variant:1361:2:   required from 'class std::variant<>'
<source>:6:36:   required from 'struct VariantContainer<>'
<source>:17:23:   required from here
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:2554:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
 2554 |     using enable_if_t = typename enable_if<_Cond, _Tp>::type;
      |           ^~~~~~~~~~~
Execution build compiler returned: 1

私の意見では、コンパイラがstd :: endlを見るVariantContainerと、ゼロのテンプレート引数でインスタンス化しようとしているように見えます(これは許可されていないためstd::variant static_assertです)。しかし、なぜコンパイラがそれを行うのでしょうか。どうすればこれを防ぐことができますか?

もちろん、これVariantContainerを防ぐためにto_stringメソッドを作成したり、-operatorのより一般的なバージョンを提供したりできることはstd::ostream <<明らかです(のようにtemplate<typename T> std::ostream& operator<<(std::ostream &, const T& ) {...}、を介して制約しstd::enable_ifます。しかし、私はこれが本当に好きではありません。

Jarod42

std::endl は関数テンプレートであるため、オーバーロードがあります。

std::cout << std::endl;

オーバーロードされた関数の(正しい)アドレスを取得する必要があります

このため、それぞれの間operator<<に署名を制約するものがあることを確認する必要があります。

確かに、basic_stream特にからのものがあります

basic_ostream& operator<<(
    std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );

(これは予想されるベストマッチです)。

しかし、他の、特に問題のあるものもチェックする必要があります。

template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);

そして、などVARIANT_TYPESの機能セットからの推論ではないでしょう、VARIANT_TYPES空のパックです。

VariantContainer<>(std::endl)関数のシグネチャを強制するかどうかチェックするのでVariantContainer<>std::variant<>が原因でハードエラーが発生するコンストラクターをインスタンス化します

今可能な解決策は、ハードエラーを回避する必要があります

template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);
  • クラスに(有効または不完全な)専門分野を提供する VariantContainer<>

  • プライマリテンプレートをに変更する

template <typename T, typename... Ts> 
class VariantContainer;

operator <<SFINAEは無効に適用されるため、変更する必要はありませんVariantContainer<>

  • 変更operator<<
template <typename T, typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<T, VARIANT_TYPES...>& inst);

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ