ジェネリックラムダタイプが与えられた場合、コンパイル時に、与えられたパラメータータイプのセットで呼び出すことができるかどうかを判断できるようにしたいと思います。次のC ++ 14実装の例があります。
#include <iostream>
// helper function; this overload handles the case that the call is possible
// use SFINAE with the extra template parameter to remove this from consideration when the
// call is ill-formed
template <typename Func, typename... Args, typename = decltype(std::declval<Func>()(std::declval<Args>()...))>
auto eval(Func f, Args &&... args) { return f(args...); }
// special type returned from `eval()` when the call can't be done
struct invalid_call { };
// helper function; this overload handles the case that the call is not possible
template <typename Func>
invalid_call eval(Func f, ...) { return invalid_call{}; };
// bring in std::negation from C++17 to help create the below trait
template<class B>
struct negation : std::integral_constant<bool, !bool(B::value)> { };
// trait that determines whether `Func` can be invoked with an argument list of types `Args...`
template <typename Func, typename... Args>
using can_call = negation<std::is_same<decltype(eval(std::declval<Func>(), std::declval<Args>()...)), invalid_call>>;
// arbitary type that has no `operator+`
struct foo {};
int main()
{
auto func = [](auto a1, auto a2) -> decltype(a1 + a2) { return a1 + a2; };
using FuncType = decltype(func);
std::cout << "can call with (int, int): " << can_call<FuncType, int, int>::value << std::endl;
std::cout << "can call with (foo, foo): " << can_call<FuncType, foo, foo>::value << std::endl;
}
この例はそのままで問題なく動作します。私が気に入らないのは、ラムダを宣言しなければならない面倒な方法です。
auto func = [](auto a1, auto a2) -> decltype(a1 + a2) { return a1 + a2; };
つまり、C ++ 14の推定戻り値の型はSFINAEで機能しないため、末尾の戻り値の型を指定する必要があります。戻り値の型の推定では、引数リストの型を呼び出し可能オブジェクトのテンプレート呼び出し演算子に置き換える必要があり、そこでエラーが発生した場合、プログラムの形式が正しくありません。
理想的には、次のことができるでしょう。
auto func = [](auto a1, auto a2) { return a1 + a2; };
戻り値の型が自動的に機能するようにします。これは、ユーザーに提供する最も直感的なインターフェイスです。これは非常に単純な例であるため、の引数decltype()
は悪くはありませんが、実際には、ラムダはいくつかのステートメントである可能性があり、このアプローチでは機能しません。だから私の質問は:
最新のC ++テクニック(C ++ 14が最適ですが、必要に応じて新しい機能も利用できます)を使用して、汎用ラムダを任意のリストで呼び出すことができるかどうかをコンパイル時にテストできる方法はありますか?パラメータタイプの?
確かに、マクロのC ++ 98機能を使用します
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { return __VA_ARGS__; }
その後
auto func = [](auto a1, auto a2) RETURNS(a1+a2);
それをします。
これを作る私たち自身の@BarryによるC ++ 20提案があります
auto func = [](auto a1, auto a2) => a1+a2;
マクロを使用せずに。
一般に、SFINAE式が受け入れ可能かどうかを判断するために、関数またはラムダの本体を強制的にコンパイルすることはできません。また、可能であるように意図されていません。このようなエラーは、C ++コンパイラの作業を簡素化するため、難しいと思われます。オーバーロードの解決が成功したかどうかを判断しながら、任意の関数の本体全体をコンパイルしてから、エラーのない状態にクリーンにバックアウトできる必要はありません。
複数のreturnステートメントまたはreturnステートメントで使用される複雑な型の長いセットの場合、運が悪いです。を書いてくださいdecltype
。あなたがそれを正しくするように祈ってください。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加