19

いくつかのクラスがあるとします:

template <typename T>
class Foo {
  const T* x_;
public:
  Foo(const T* str) : x_{str} {}
};

Fooそして、オブジェクトを作成するユーザー定義のリテラルをいくつか提供します。

Foo<char> operator"" _foo(const char* str, std::size_t) {
  return Foo<char>{str};
}

Foo<wchar_t> operator"" _foo(const wchar_t* str, std::size_t) {
  return Foo<wchar_t>{str};
}

// etc. for char16_t and char32_t.

私の質問は次のとおりです。これらをテンプレート化して、コードを書き直す必要がないのはなぜですか?

template <typename T>
Foo<T> operator"" _foo(const T* str, std::size_t) {
  return Foo<T>{str};
}

gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) および 7.0.0 (自分でコンパイル) レポート:

error: ‘Foo<T> operator""_foo(const T*, std::size_t)’ has invalid argument list
Foo<T> operator"" _foo(const T* str, std::size_t) {
                                                ^

エラー メッセージは十分に明確に見えますが、原則としてこれを許可してはならない理由がわかりません。それで、私はこれを間違ってやっていますか、それとも本当に許可されていませんか?

4

1 に答える 1

16

これを考慮してください:

リテラル演算子がテンプレートの場合、空のパラメーター リストが必要であり、要素型が char の非型テンプレート パラメーター パックである必要があるテンプレート パラメーターを 1 つだけ持つことができます。

つまり、リテラル演算子テンプレートの宣言は次のようにする必要があります。

template <char...> double operator "" _x();

それはあなたの場合ではありません。


私は言語弁護士ではありませんが、あなたのケースに関連する標準のセクションは[over.literal] (作業草案へのリンク) であると思います。

[over.literal]/2からの抜粋は次のとおりです。

literal-operator-id で宣言された関数テンプレートは、リテラル オペレーター テンプレートです。

以下[over.literal]/5が引用されています:

リテラル演算子テンプレートの宣言には、空の parameter-declaration-clause があり、その template-parameter-list には、要素型が char の非型テンプレート パラメーター パック ([temp.variadic]) である単一の template-parameter が必要です。 .

問題のような宣言は、標準では明示的に禁止されているようです。
より一般的には、リテラル演算子を宣言する関数テンプレートは、指定されたパターンに厳密に従う必要があります。


私はこれを間違ってやっていますか、それとも本当に許可されていませんか?

それは本当に許されないと思います。

とにかく、各演算子で繰り返したくない複雑なロジックがある場合は、テンプレート関数を引き続き使用できます。

template<typename T>
Foo<T> create(const T *str) {
    // your logic...
    return Foo<T>{str};
}

Foo<char> operator"" _foo(const char *str, std::size_t) {
    return create(str);
}

Foo<wchar_t> operator"" _foo(const wchar_t *str, std::size_t) {
    return create(str);
}

これは、間接的な追加レイヤーの問題であり、それだけです。
明らかに、すべての演算子が 1 行の本体関数である場合は価値がありません。

于 2017-01-06T13:45:00.113 に答える