指定されたテンプレートパラメータの合計を計算する可変引数テンプレートconstexpr
関数を記述しようとしています。これが私のコードです:
_template<int First, int... Rest>
constexpr int f()
{
return First + f<Rest...>();
}
template<int First>
constexpr int f()
{
return First;
}
int main()
{
f<1, 2, 3>();
return 0;
}
_
残念ながら、f<3,>()
の呼び出しを解決しようとすると、エラーメッセージ_error C2668: 'f': ambiguous call to overloaded function
_が報告されてコンパイルされません。
また、再帰ベースケースを変更して、1ではなく0のテンプレート引数を受け入れるようにしました。
_template<>
constexpr int f()
{
return 0;
}
_
しかし、このコードもコンパイルされません(メッセージerror C2912: explicit specialization 'int f(void)' is not a specialization of a function template
)。
次のように、最初と2番目のテンプレート引数を抽出して、コンパイルして機能させることができます。
_template<int First, int Second, int... Rest>
constexpr int f()
{
return First + f<Second, Rest...>();
}
_
しかし、これは最良の選択肢ではないようです。したがって、問題は、この計算をエレガントな方法でどのように書くかです。
[〜#〜] up [〜#〜]:これも単一の関数として記述しようとしました:
_template<int First, int... Rest>
constexpr int f()
{
return sizeof...(Rest) == 0 ? First : (First + f<Rest...>());
}
_
そして、これも機能しません:_error C2672: 'f': no matching overloaded function found
_。
あなたのベースケースは間違っていました。空のリストのケースが必要ですが、コンパイラーが示唆するように、2回目の試行は有効なテンプレートの特殊化ではありませんでした。ゼロ引数の有効なインスタンス化を定義する1つの方法は、空のリストを受け入れるオーバーロードを作成することです
template<class none = void>
constexpr int f()
{
return 0;
}
template<int First, int... Rest>
constexpr int f()
{
return First + f<Rest...>();
}
int main()
{
f<1, 2, 3>();
return 0;
}
編集:完全性のために、私の最初の答えも、条件付きを追加することによって@ alexeykuzmin0が修正されました:
template<int First=0, int... Rest>
constexpr int f()
{
return sizeof...(Rest)==0 ? First : First + f<Rest...>();
}
template<int First, int... Rest> constexpr int f() { return First + f<Rest...>(); } template<int First> constexpr int f() { return First; } int main() { f<1, 2, 3>(); return 0; }
あなたはこのエラーを受け取ります:
error C2668: 'f': ambiguous call to overloaded function while trying to resolve f<3,>() call.
これは、可変パラメータパックに0の引数を与えることができるため、f<3>
はtemplate<int First, int... Rest>
でtemplate<3, >
に「拡張」することで機能します。ただし、template<int First>
の特殊化も行っているため、コンパイラーはどちらを選択するかわかりません。
最初と2番目のテンプレート引数を明示的に述べることは、この問題に対する完全に有効で優れた解決策です。
基本ケースを次のように変更しようとすると、
template <>
constexpr int f()
{
return 0;
}
関数はそのように特化できないため、問題が発生します。クラスと構造体は可能ですが、関数はできません。
constexpr
を使用したC++ 17折りたたみ式template <typename... Is>
constexpr int sum(Is... values) {
return (0 + ... + values);
}
constexpr
関数を使用するconstexpr int sum() {
return 0;
}
template <typename I, typename... Is>
constexpr int sum(I first, Is... rest) {
return first + sum(rest...);
}
template <int... Is>
struct sum;
template <>
struct sum<>
: std::integral_constant<int, 0>
{};
template <int First, int... Rest>
struct sum<First, Rest...>
: std::integral_constant<int,
First + sum_impl<Rest...>::value>
{};
コードをテンプレート引数から関数引数に移動する方が一般的に簡単だと思います。
constexpr int sum() { return 0; }
template <class T, class... Ts>
constexpr int sum(T value, Ts... rest) {
return value + sum(rest...);
}
それらをテンプレート引数として本当に必要な場合は、f
を下に移動してsum
を呼び出すだけです。
template <int... Is>
constexpr int f() {
return sum(Is...);
}
constexpr
なので、int
sを使用するだけで十分です。
通常の方法で要約します。
template<int... Args>
constexpr int f() {
int sum = 0;
for(int i : { Args... }) sum += i;
return sum;
}
std::initializer_list
を使用したより一般的なソリューションは次のとおりです。
template <typename... V>
auto sum_all(V... v)
{
using rettype = typename std::common_type_t<V...>;
rettype result{};
(void)std::initializer_list<int>{(result += v, 0)...};
return result;
}
これは、@ T.Cが上で提案したものと非常に似ています。
#include<iostream>
#include<numeric>
template<int First, int... Rest>
constexpr int f()
{
auto types = {Rest...};
return std::accumulate(std::begin(types), std::end(types),0);
}
int main()
{
std::cout <<f<1, 2, 3>();
return 0;
}