次のコードがあります。ここでは、を含み、VariantContainerの可変個引数テンプレート引数を使用する可変個<<
引数テンプレート引数タイプのostream演算子オーバーロードを定義してVariantContainer
います。std::variant
std::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
ます。しかし、私はこれが本当に好きではありません。
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]
コメントを追加