web-dev-qa-db-ja.com

C ++が強く型付けされた省略記号をサポートしないのはなぜですか?

誰かが私に、少なくとも私の知る限り、C++が強く型付けされたEllipsis関数を実装しない理由を説明してもらえますか?

void foo(double ...) {
 // Do Something
}

つまり、ユーザーはfoo関数に可変数の用語を渡すことができますが、すべての用語はdoubleでなければなりません。

29

歴史的に、Ellipsis構文_..._はCから来ています。

この複雑な獣は、printfのような関数を強化するために使用され、_va_list_、_va_start_などで使用されます...

お気づきのように、これはタイプセーフではありません。しかし、Cはタイプセーフではありません。ポインタ型の_void*_との間の暗黙的な変換、整数/浮動小数点値の暗黙的な切り捨てなどにより...

C++はCのスーパーセットとして可能な限り近くなるため、Cから省略記号を継承しました。


創業以来、C++のプラクティスは進化し、より強力なタイピングへの強いプッシュがありました。

C++ 11では、これは最高潮に達しました:

  • 初期化子リスト、特定のタイプの可変数の値の省略構文:foo({1, 2, 3, 4, 5})
  • 独自の野獣であり、タイプセーフなprintfを記述できる可変型テンプレート

Variadicテンプレートは実際に、構文でEllipsis _..._を再利用して、型または値のpacksを示し、アンパック演算子として使用します。

_void print(std::ostream&) {}

template <typename T, typename... Args>
void print(std::ostream& out, T const& t, Args const&... args) {
    print(out << t, args...); // recursive, unless there are no args left
                              // (in that case, it calls the first overload
                              // instead of recursing.)
}
_

_..._の3つの異なる使用法に注意してください。

  • _typename..._可変個の型を宣言する
  • _Args const&..._は、引数のパックを宣言します
  • _args..._パックを式に解凍する
15
Matthieu M.

有る

 void foo(std::initializer_list<double> values);
 // foo( {1.5, 3.14, 2.7} );

それに非常に近いです。

可変テンプレートを使用することもできますが、それはより議論的なものになります。実際の理由については、その新しい構文を取り込む努力はおそらくそれだけの価値はないと思います。単一の要素にどのようにアクセスするのですか?いつ停止するかをどうやって知っていますか?何より、たとえばstd::initializer_list

C++には、これにさらに近いものがあります。非型パラメーターパックです。

template < non-type ... values>

のように

template <int ... Ints>
void foo()
{
     for (int i : {Ints...} )
         // do something with i
}

ただし、非タイプテンプレートパラメータ(uhm)のタイプにはいくつかの制限があります。たとえば、doubleにすることはできません。

23
edmz

可変テンプレートとSFINAEですでに可能です:

template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template <class... Doubles, class = std::enable_if_t<
    all_true<std::is_convertible<Doubles, double>{}...>{}
>>
void foo(Doubles... args) {}

ありがとう コロンボ ニースのためにall_true 騙す。また、C++ 17でフォールド式を使用することもできます。

将来の標準では、terser構文(簡潔なforループ、暗黙の関数テンプレート...)に焦点が当てられているため、提案された構文が標準で1日になる可能性が非常に高くなります;)

11
Quentin

具体的にそのようなことが提案されなかった(または提案されて拒否された)理由については、わかりません。このようなことは確かに便利ですが、言語をさらに複雑にします。 Quentin が示すように、テンプレートでそのようなことを実現するC++ 11の方法はすでに提案されています。

コンセプトが標準に追加されると、別のより簡潔な方法が得られます。

template <Convertible<double>... Args>
void foo(Args... doubles);

または

template <typename... Args>
    requires Convertible<Args, double>()...
void foo(Args... doubles);

または @ dypが指摘するように

void foo(Convertible<double>... doubles);    

個人的には、現在の解決策とConceptsで得られる解決策の間で、それが問題に対する適切な解決策だと感じています。特に最後のものは基本的にあなたがもともと求めていたものだからです。

2
Barry
template<typename T, typename... Arguments>
struct are_same;

template <typename T, typename A1, typename... Args>
struct are_same<T, A1, Args...>{    static const bool value = std::is_same<T, A1>::value && are_same<T, Args...>::value;};

template <typename T>
struct are_same<T>{static const bool value = true;};

template<typename T, typename... Arguments>
using requires_same = std::enable_if_t<are_same<T, Arguments...>::value>;

template <typename... Arguments, typename = requires_same<double, Arguments...>>
void foo(Arguments ... parameters)
{
}
1
Simon Kraemer

マシューの答え に基づく:

void foo () {}

template <typename... Rest>
void foo (double arg, Rest... rest)
{
    /* do something with arg */
    foo(rest...);
}

fooを使用するコードがコンパイルされると、すべての引数がdoubleに変換可能であることがわかります。

1
jxh

あなたが提案することの(一種の)達成方法は、可変テンプレートを使用することです

template<typename... Arguments>
void foo(Arguments... parameters);

ただし、パラメーターパックで任意の型を渡すことができます。あなたが提案したことは実装されたことはありません。多分それは言語への大きな追加かもしれませんし、現状では実装するのが難しすぎるかもしれません。あなたはいつも提案を書いて、それをisocpp.orgに提出することを試みることができます

1
dau_sama

使えるから

void foo(std::vector<T> values);
0
SingerOfTheFall