C ++が2番目のテンプレート引数を推測しないようにするにはどうすればよいですか?


26

私はC ++ライブラリ(strf)を使用していますが、その中には、次のコードが含まれています。

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

今、strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)私のコードで使用したいと思います。しかし、そのようにすると、次のエラーが発生します(CUDA 10.1のNVCCを使用)。

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

ライブラリのコードは、おそらく使用して例えば(これを避けるために変更することができます。

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

Rangeポインタでないことを確認するため); 今は変更できません。代わりに、テンプレート引数を1つだけ指定し、1つは指定せず、もう1つは演繹しないことをコンパイラーに何らかの方法で示す必要があります。

それをしてもいいですか?

C ++ 11とC ++ 14の回答をいただければ幸いです。控除ガイドを含むC ++ 17の回答はそれほど重要ではありませんが、ある場合は投稿してください(将来のNVCCバージョンの場合...)


更新:この状況を回避するためにstrfライブラリ自体が更新されましたが、質問は尋ねられたとおりです。


1
薄くラップするカスタムのイテレータを渡すと思いますが、char*それは解決策ではありませんか?
Konrad Rudolph

1
@KonradRudolph:これは回避策ですが、私の質問には答えません。私は既に別の回避策(/*...*/の内容に固有)をすでに持っていますが、ここで重要な道を進みたいと思います。
einpoklum

1
その場合、残念ながら私の(推測)答えは「できない」です。公平を期すために、自分のコードで提案された回避策を受け入れるかどうかはわかりません。
Konrad Rudolph

明確にするために:1つのvs 2つのパラメーターを持つテンプレートオーバーロード間の呼び出しを区別するために常に機能する一般的なソリューションが必要ですか、それともこのケースに固有のソリューションのみが必要ですか?
ウォールナット

@walnut:一般的なソリューションの方が良いでしょう。私の特定のシナリオは、主に問題の動機です。
einpoklum

回答:


16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

次にのrange1代わりに呼び出しますstrf::range

range1_ptr<T>(...)常に1つのテンプレート引数を使用してテンプレートを明示的に呼び出すために使用できますが、引数からの推定は行いません。range1元のstrf::rangeテンプレートから控除を複製します。

これが機能するのは、[temp.deduct.funcaddr] / 1が、変換のターゲットタイプなしで関数のアドレスを取得するときのテンプレート引数の推定は、仮想呼び出しのパラメーターと引数のリストが空の。したがって、2番目のテンプレート引数は、2つのテンプレートパラメーターを持つ2番目のオーバーロードに対して推定できません。残っている唯一の候補は最初のオーバーロードであり、関数ポインターのターゲットとして選択されます。

引数が1つだけの有効なtemplate-idを形成できる2番目の候補関数テンプレートがない限り、range1_ptr常に1つの引数を取る関数テンプレートを明確に呼び出すために使用できます。そうしrange1_ptrないと、あいまいさのためにのインスタンス化でエラーが発生します。


あいまいさがありませんstrf::range<T>か?
einpoklum

1
@einpoklum GCCとClangで正常にコンパイルされます。私は標準をチェックしませんでしたが、それがあいまいであることになっているとしたら驚きます。
ウォールナット

関数名をpretty_please_with_sugar_on_top()?に変更する必要があるかもしれません。... C ++は時々とても奇妙なことがあります...
einpoklum

あなたは何とか受け入れられた回答を取り除きました:-P
einpoklum

11

を通過するのはusingどうですか?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

そしてこれはコンパイルしますか?2行目は特に疑わしく見えます。
einpoklum

@einpoklum-面白いですね。
max66

@einpoklum-残念ながら一般的な解決策ではありません。(私が間違っていない場合)最初のrange()バージョンのみがと互換性があるため、この場合は機能しtpfます。他のケースは異なる場合があります。
max66

@einpoklum-2行目では、テンプレートパラメータを説明することもできます(tfp x = &strf::range<char const *>;); このようにして、あなたが一般的な解決策を持っていると思います、クルミの解決策とほぼ同等です
max66 '30

0

解決策は

1)まず、2番目の引数の型を指定する必要があります。例: (char *)(some_char_ptr + some_length)

2)const両方に使用しないでください、これはうまくいきます:

strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

あなたが交換するために試みることができる(char *)(const char *)左または右に、それはまだ動作します。


引数がconstデータを指している場合、これはかなり醜いです。
アシェプラー

1. 2番目の引数はありません。単一引数のテンプレートが必要です。2.面白いハック!最初の提案は-1、2番目の提案は+1 :-P
einpoklum
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.