役立つエラーやメッセージを提供したいのですが、static_assert
sについても提供したいと思っています。問題は、テンプレートパラメータに依存することです。通常、これらのパラメータは発生したエラーのため途中で表示されますが、意味が分からないかグループ化されていないため、意味があります。例:
template<class T>
struct fake_dependency{
static bool const value = false;
};
template<class T, class Tag>
struct Foo{
Foo(){}
template<class OtherTag>
Foo(Foo<T, OtherTag> const&){
static_assert(fake_dependency<T>::value, "Cannot create Foo<T,Tag> from Foo<T,OtherTag>.");
}
};
int main(){
Foo<int, struct TagA> fA;
Foo<int, struct TagB> fB(fA);
}
MSVCでの出力:
src\main.cpp(74): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
src\main.cpp(84) : see reference to function template instantiation 'Foo<T,Tag>::Foo<main::TagA>(const Foo<T,main::TagA> &)' being compiled
with
[
T=int,
Tag=main::TagB
]
1つのタグは関数テンプレート自体で言及され、もう1つのタグはクラステンプレートで言及されます。いいね。 GCC出力 を見てみましょう:
prog.cpp: In constructor 'Foo<T, Tag>::Foo(const Foo<T, OtherTag>&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]':
prog.cpp:18:32: instantiated from here
prog.cpp:12:5: error: static assertion failed: "Cannot create Foo<T,Tag> from Foo<T,OtherTag>."
はるかに良いですが、実際にはstatic_assert
がどこにあるかはわかりません。そして、さらにいくつかのパラメーター、またはより多くのテンプレート、あるいはその両方を想像してみてください。 shivers
これを回避する1つの方法は、中間構造体を使用することです。これは、両方のタグをテンプレートパラメータとして使用します。
template<class Tag, class OtherTag>
struct static_Foo_assert{
static_assert(fake_dependency<Tag>::value, "Cannot create Foo<T,Tag> from Foo<T,OtherTag>.");
};
template<class T, class Tag>
struct Foo{
Foo(){}
template<class OtherTag>
Foo(Foo<T, OtherTag> const&){
static_Foo_assert<Tag, OtherTag> x;
}
};
出力をもう一度見てみましょう:
src\main.cpp(70): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
src\main.cpp(79) : see reference to class template instantiation 'static_Foo_assert<Tag,OtherTag>' being compiled
with
[
Tag=main::TagB,
OtherTag=main::TagA
]
ずっといい! GCCによると :
prog.cpp: In instantiation of 'static_Foo_assert<main()::TagB, main()::TagA>':
prog.cpp:17:40: instantiated from 'Foo<T, Tag>::Foo(const Foo<T, OtherTag>&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]'
prog.cpp:23:32: instantiated from here
prog.cpp:8:5: error: static assertion failed: "Cannot create Foo<T,Tag> from Foo<T,OtherTag>."
悪くないですね。問題:static_assert
のエラーメッセージは文字列リテラルである必要があるため、すべてのテンプレートにこのような構造体を作成する必要があります...
さて、私の質問について:型名をstatic_assert
に直接含めることができますか?お気に入り
static_assert(..., "Cannot create Foo<" T "," Tag "> from Foo<" T "," OtherTag ">.");
出力例:
Foo<int,main::TagA>
からFoo<int,main::TagB>
を作成できません。
または、それが達成できない場合は、エラーメッセージを渡せるようにするために、なんらかの理由でエラーメッセージを追加のテンプレートパラメータにできますか?
template <typename Assertion>
struct AssertValue : AssertionChecker<Assertion::value, Assertion>
{
static_assert(AssertionValue, "Assertion failed <see below for more information>");
static bool const value = Assertion::value;
};
::value
アサーション、および失敗した場合はタイプをダンプします。
// Bad indentation used to show parts
static_assert(
AssertValue<
std::my_check<
T0, decltype(*somethingComplicated), T7::value_type
>
>,
"something horrible happened"
);
どこ std::my_check<...>::value
はチェックのブール結果です
完全な [〜#〜] sscce [〜#〜] の例については、以下を参照してください。 IDEOneの例
例のエラーメッセージ:
prog.cpp: In instantiation of 'AssertValue<std::is_base_of<IMyInterface, MyBadType> >':
prog.cpp:37:69: instantiated from 'void MyFunction(IteratorType, IteratorType) [with IteratorType = __gnu_cxx::__normal_iterator<MyBadType*, std::vector<MyBadType> >]'
prog.cpp:60:38: instantiated from here
prog.cpp:9:5: error: static assertion failed: "Assertion failed <see below for more information>"
prog.cpp: In function 'void MyFunction(IteratorType, IteratorType) [with IteratorType = __gnu_cxx::__normal_iterator<MyBadType*, std::vector<MyBadType> >]':
prog.cpp:60:38: instantiated from here
prog.cpp:39:5: error: static assertion failed: "iterator passed does not reference IMyInterface items"
アサーションが失敗した場合、AssertValueのテンプレート引数が出力されるため、チェックの完全なテンプレート展開が出力されます。たとえば、std::is_base_of
チェックの完全なタイプを出力します。例:std::is_base_of<IMyInterface, MyBadType>
。次に、失敗したアサーションで使用されたタイプを正確に把握します。
唯一の問題は、これが結果を::value
。しかしながら type_traits
主にこれを使用し、goto標準です。
hoop-jumping を少し使用して、文字列リテラルをテンプレートの非型パラメーターとして渡すことができます。ただし、static_assert
の2番目の引数は、たとえばアドレス定数式ではなく文字列リテラルに制限されているため、残念ながらこれはあまり役に立ちません。
悲しいことに、私はあなたの最善の策は委員会やコンパイラーの作者にロビーを拡張して施設を拡張することだと思います。
コンパイラが__FUNCTION__
マクロを使用すると、それとリテラル連結を使用して本当に簡単な置換を行うことができます。ただし、gccとclangの実装はマクロとして実行されないため、このソリューションは機能しません。
#include "stdafx.h"
#include <type_traits>
template <class T>
class must_be_pod
{
static void test() { static_assert (std::is_pod<T>::value, __FUNCTION__ ": not a POD"); }
public:
must_be_pod() { test(); }
};
class not_a_pod
{
public:
not_a_pod() {}
virtual ~not_a_pod() {}
};
int main()
{
must_be_pod<not_a_pod> should_fail; // and it does
return 0;
}
VS2015でコンパイルすると、次の出力が生成されます。
static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD
私はこれが少し前に回答されたことを確認しますが、完全な回答は失われ、望ましい結果を達成するための非常に簡単な方法が見つかりました。
template <typename T, bool value>
static typename std::enable_if<value, void>::type FunctionWithReadableErrorMessage()
{
}
int main()
{
FunctionWithReadableErrorMessage<int, false>();
return 0;
}
この関数はコンパイルされ、value = trueの場合は効果がありません。それ以外の場合、次のエラーメッセージが表示されます。
main.cpp:関数 'int main()':main.cpp:16:50:エラー: 'FunctionWithReadableErrorMessage()'への呼び出しに一致する関数がありませんFunctionWithReadableErrorMessage(); ^
もう少し一般的にしたい場合は、マクロに入れることができます