この質問とこの他の質問を組み合わせて、次の(実際には非常に単純な)解決策にたどり着きました。アイデアは、実際の関数の範囲内でのみ型エイリアスを使用可能にし、適切なポイントでテンプレートの条件を確認することです。
template<typename... garbage>
struct firewall
{
typedef typename std::enable_if<sizeof...(garbage) == 0>::type type;
};
#define FIREWALL_CALL typename = typename firewall<garbage...>::type
#define TMPL_FIREWALL typename... garbage, FIREWALL_CALL
#define TMPL_ALIAS typename
#define TMPL_CONDITION(...) typename = \
typename std::enable_if<__VA_ARGS__::value>::type
このコードを使用すると、快適な方法で必要なエイリアスまたは条件を追加できます。このコード:
// Erase only qualifiers and references.
template<typename target>
struct pluck
{
typedef typename std::remove_cv<
typename std::remove_reference<target>::type>::type type;
}
// `some_fun` wants ensure its arguments are passed by non-constant values.
template<typename T>
typename pluck<T>::type
some_fun(typename pluck<T>::type a, typename pluck<T>::type b)
{
typename pluck<T>::type c;
// something
return c;
}
になります(のみsome_fun
):
template<typename T, TMPL_FIREWALL,
TMPL_ALIAS friendly = typename pluck<T>::type
TMPL_CONDITION(std::is_copy_constructible<friendly>)>
friendly some_fun(friendly a, friendly b)
{
friendly c;
// something
return c;
}
上記の 2 番目の質問で@ipcfirewall
が示しているように、の目的は、デフォルトのテンプレート引数として定義されたローカル型エイリアスを再配置できる引数を吸収することです。
また、これにより、他のより深い問題を回避するための便利な方法が利用可能になります。事前に既知のタイプの完全な転送を行うためだけに関数をパラメトリックにしたい場合。たとえば、次の状況では:
struct A
{
template<typename... Args>
A(Args&&... args) : _b(std::forward<Args>(args)...)
{}
template<typename Str>
A(Str&& str) : _str(std::forward<Str>(str))
{}
B _b;
std::string _str;
};
完全な転送メカニズムを使用して初期化する場合_str
、他のテンプレート引数とあいまいになることは避けられません。これは、次の追加マクロを使用して簡単に回避できます。
#define TMPL_PURE_FORWARDING(a, b) TMPL_FIREWALL, \
TMPL_CONDITION(std::is_same<typename _f_pluck<a>::type, b>)
struct A
{
template<typename... Args>
A(Args&&... args) : _b(std::forward<Args>(args)...)
{}
template<typename fwStr, TMPL_PURE_FORWARDING(fwStr, std::string)>
A(fwStr&& str) : _str(std::forward<fwStr>(str))
{}
B _b;
std::string _str;
};
fwStr
タイプstd::string
、std::string&
、またはそれらの定数バージョンでない場合はstd::string&&
、他のコンストラクターが選択され、他のコンストラクターがない場合は、std::enable_if<false, void>::type
存在しないというコンパイラ エラーがスローされます。
質問: C++ では常にマクロの使用を避けることが望ましいですが、よく知られているテンプレートは冗長であり、これらの状況 (特に 2 番目の状況) は非常に一般的です。少なくとも私の経験では。次に、これらのマクロは非常に便利です。
この状況でマクロを使用するのは危険ですか? これは一般的に良いものですか、それとも有用ですidiom
か、それとも何もないようですか?