28

前置演算子と後置演算子の両方として使用できる演算子を作成しようとしました

#include <iostream>
#include <utility>

struct B { 
  // ...
};

template<typename ...T>
void operator++(B, T...) {
  std::cout << ((sizeof...(T) == 0) ? "prefix" : "postfix") << std::endl;
}

int main() {
  B b;
  b++;
  ++b;
}

GCCはこれでコンパイルして正常に動作しますが、clangは言います

main.cpp:9:24: エラー: オーバーロードされた後置インクリメント演算子のパラメーターには、型 'int' が必要です ('T...' ではありません)

void operator++(B, T...) {

誰が正しいですか?


GCC の動作を理解するのを手伝ってくれた人に感謝します。新しい Clang バグ レポートを提出しました。

http://llvm.org/bugs/show_bug.cgi?id=14995

4

1 に答える 1

7

元の回答: (有用な情報が含まれている可能性があるため削除されません)

要するに、オーバーロードされたオペレーターテンプレートがオーバーロードされたオペレーターと見なされるかどうかにかかっていると言えます。論理的には、これは当てはまらず、Clang は間違っていると思います。名前と署名の互換性に基づいて、まずテンプレートオーバーロード解決の候補として選択し、次にインスタンス化し、(おそらく) 選択する必要があると思います。私の見方では、インスタンス化の後でのみ、コンパイラは結果の関数に適切な量の引数があるかどうかを確認する必要があります。

しかし、それは私の意見です。postfix のオーバーロードに関する § 13.5.7/1 によるとoperator ++:

「関数が 1 つのパラメーター (int 型である必要があります) を持つメンバー関数であるか、2 つのパラメーター (2 番目のパラメーターは int 型である必要があります) を持つ非メンバー関数である場合、オブジェクトに対して後置インクリメント演算子 ++ を定義します。そのタイプの」

標準は、関数テンプレートが正当な演算子のオーバーロードの署名に関する制限に関する関数と見なされるかどうかを明確にしていないようです(少なくとも、このあいまいさを解決する文は見つかりませんでした)。これが本当である限り、この質問に明確な答えを与えることはほとんどなく、私たちは意見を残しています.

しかし、この問題に関連するもう 1 つの側面、一貫性について言及したいと思います。

質問のテキストのコードが Clang でコンパイルされないことは事実ですが、次のコードはコンパイルされます。

template<typename... Ts>
int operator + (X x1, Ts... args)
{
    return 0;
}

2 つのケースの間に概念上の違いは見当たりません。インスタンス化の前に演算子のオーバーロードのシグネチャをチェックする場合、上記の定義もコンパイルすべきではありません。そうでない場合は、質問のテキストのコードをコンパイルする必要があります。

したがって、私の意見では、答えは GCC が正しいか、どちらも間違っているということです。

アップデート:

@JesseGood と @SethCarnegie が 14.7/4 に従って正しく指摘しているように:

「特殊化とは、インスタンス化または明示的に特殊化されたクラス、関数、またはクラス メンバーです。」

さらに、14.6/8 あたり:

「有効な特殊化を生成できるテンプレートに対しては、診断は発行されません。」

したがって、Clangは実際には間違っているようで、質問のテキストの演算子関数テンプレートに対してコンパイルエラーは発生しません。

于 2013-01-17T11:38:46.133 に答える