2

編集済み - 実際の問題がある編集にスキップしてください

スタンドアロン関数の文字列ヘルパー ライブラリで、 を受け取るバージョンと . を受け取るバージョンで関数のオーバーロードを提供する状況によく遭遇しcharますstd::string

問題は、文字列リテラル ( ) を渡すとオーバーロードがあいまいになることconst char*です。

例:

void myFunc(const std::string &subStr);
void myFunc(char character);

これら 2 つの関数は異なる方法で実装されており、1 つは文字列用に最適化され、もう 1 つは単一の文字用に最適化されています。myFunc("literal")ただし、常にバージョンを呼び出したいと思っていたにもかかわらず、呼び出そうとするとあいまいな結果になりstd::stringます。

これvoid myFunc(const char *str)により、次のようなスタブのみであるオーバーロードのバージョンを提供する必要があります。

void myFunc(const char *str)
{
    myFunc(std::string(str));
}

これらのスタブ関数を不要にする方法はありますか? 「明示的」にできるようにしたいvoid myFunc(char c)のですが、コンストラクター以外の非メンバー関数を明示的にすることはできません。これは問題を即座に解決します。=(...

(余談ですが、スタンドアロン関数を明示的にできないのはなぜですか?)

編集: プログラマーが夜遅くまでコーディングしすぎることについて彼らが言っていることをご存知でしょう! (冗談を覚えているなら教えてください。最初に聞いたときは眠すぎて忘れてしまったからです)

本当の問題

私はMinGW v4.7.2を使用していますが、問題は私の投稿が当初想定していたものとは大きく異なりました。

問題は、いくつかのオーバーロードがあることです。はい、この例は正常に動作します:

void myFunc(const std::string &subStr);
void myFunc(char character);

しかし、std::function オーバーロードを追加すると、次のように壊れます。

void myFunc(const std::string &subStr);
//Not actually part of the problem; I was confused by part of the error message highlighting this function.
//void myFunc(char character); 
void myFunc(std::function<bool(char)); //<-- The real problem

私の文字列ライブラリには、std::string、char、および std::function のオーバーロードがあります (場合によっては、多数のオプション パラメーターを使用して関数を単純化するためのオーバーロードがさらにいくつかあります)。

std::function をオーバーロードとして使用すると、次のエラー メッセージが表示されます。

error: call of overloaded ‘myFunc(const char [15])’ is ambiguous
candidates are:
void myFunc(char) <near match>
no known conversion for argument 1 from ‘const char [15]’ to ‘char’
void myFunc(std::function<bool(char)>)
void myFunc(const string&)

myFunc(char) は、私が昨夜最初に混乱した方法です。コードからそのオーバーロードを削除すると、次のエラー メッセージが表示されます。

error: call of overloaded ‘myFunc(const char [15])’ is ambiguous
candidates are:
void myFunc(std::function<bool(char)>)
void myFunc(const string&)

これは、自己完結型のコンパイル可能な例です。

文字列リテラルに std::function ではなく std::string を選択させるにはどうすればよいですか? std::function のコンストラクターはテンプレート化されており、特に関数ポインターを取るように設計されているため、おそらくあいまいです。

私の文字列ライブラリは、具体的には、すでに typedef されているstd::function<bool(char)>andのみを使用std::function<bool(const std::string&)>しているため、明示的なコンストラクターを使用してそれらをクラスに継承できます。

利用可能な他の提案やオプションはありますか?

4

3 に答える 3

1

これで非常に簡単に SFINAE を実行できるはずです。

template <typename F>
auto myFunc(F f) -> decltype(!f('0'), std::function<bool(char)>(f), void()) {
    std::cout << "std::function<bool(char)> overload" << std::endl;
}

または、C++03 コンパイラを使用します (コンパイラにまだない場合は、おそらく tr2/type_traits または Boost Type Traits を使用します):

template <typename F>
void myFunc(F f, typename std::enable_if<std::is_constructible<
       std::function<bool(char)>, F>::value>::type* = nullptr)
{
    std::cout << "std::function<bool(char)> overload" << std::endl;
}


動作することの証明: http://ideone.com/Q87JsV

#include <iostream>
#include <type_traits>
#include <functional>

#if 1
template <typename F>
auto myFunc(F f) -> decltype(!f('0'), std::function<bool(char)>(f), void()) {
    std::cout << "std::function<bool(char)> overload" << std::endl;
}
#else
template <typename F>
void myFunc(F f, typename std::enable_if<std::is_constructible<
       std::function<bool(char)>, F>::value>::type* = nullptr)
{
    std::cout << "std::function<bool(char)> overload" << std::endl;
}
#endif


void myFunc(const std::string &seperator) {
    std::cout << "std::string overload" << std::endl;
}

bool testCallback(char) {
    return true;
}

int main()
{
    myFunc("String literal");
    myFunc(std::string("std::string"));
    myFunc(testCallback);
}

出力:

std::string overload
std::string overload
std::function<bool(char)> overload
于 2013-07-11T22:09:27.603 に答える
1

自己完結型の再現を提供しなかったため、何が問題なのかはわかりません。しかし、ここにいくつかの推測があります:

  1. コンパイラにバグがある (可能性は低い)
  2. 文字のオーバーロードを適切に呼び出していません。myFunc('c') のように呼び出す必要があります。
  3. 間違った呼び出しコードまたは間違ったメソッド シグネチャを提供しました。

次のコード スニペットは、何が起こっているのか、メソッドを適切に宣言して呼び出す方法を説明する必要があると思います。リテラルをキャプチャする myOtherFunc トリックに注意してください。文字列の周りにスマートラッパーを使用して、テンプレート機能なしでより適切に実行できますが、それは省略します。

コンパイラで試してみて、それが機能するかどうかを確認することもできます。そうすれば、コンパイラに問題があるかどうかがわかります。

ライブ コード: http://codepad.org/gzB7xWs2

#include <string>
#include <iostream>

using namespace std;

void myFunc(char c) {
    cout << "myFunc called with char" << endl; 
}

void myFunc(const string& s) {
    cout << "myFunc called with string" << endl;
}

void myOtherFunc(char c) {
    cout << "myOtherFunc called with char" << endl; 
}

void myOtherFunc(const string& s) {
    cout << "myOtherFunc called with string" << endl;
}

template <size_t StingSizeWithNullTerminator>
void myOtherFunc(const char (&buf)[StingSizeWithNullTerminator]){
    cout << "myOtherFunc called with literal of size " << (StingSizeWithNullTerminator - 1) << endl;
}

int main() {
    myFunc("string");
    myFunc('c');
    myFunc(string("std string"));

    myOtherFunc("string");
    myOtherFunc('c');
    myOtherFunc(string("string"));

    return 0;
}

出力:

myFunc called with string
myFunc called with char
myFunc called with string
myOtherFunc called with literal of size 6
myOtherFunc called with char
myOtherFunc called with string

アップデート

さて、この例では、問題が何であるかは明らかです。問題は、char[15] を受け入れる正確なシグネチャを持つメソッドがないことです。また、コンパイラは変換を実行する必要があります。問題は、std::string または std::function に変換できることです (std::function には、char[15] を含む任意の型を受け入れるテンプレート コンストラクターがあるため)。したがって、使用する変換を選択できず、あきらめます。

したがって、私が知る限り、そのためのきれいな解決策はありませんが、ここではそれほどきれいではありません:

  1. メソッドの呼び出し時に std::string への明示的な変換を使用する
  2. 文字列と関数の両方を受け入れる myFunc を使用する理由は何なのか、自問してみてください (そして、私たちに教えてください)。おそらく設計に問題があり、同じ名前の関数を持つことを避けることができます。
  3. bool(&)(char) 関数のみを受け入れる必要がある場合は、代わりにカスタム ラッパーを使用できます (以下の例を参照)。

3 番目のオプションの例 ( http://ideone.com/o0NqUf ):

#include <iostream>
#include <functional> //Required for std::function.

struct Callback
{
    Callback(bool (&func)(char)): m_func(func)
    {}

    bool operator()(char c) { return m_func(c); }
    bool (&m_func)(char);
};

void myFunc(Callback seperatorFunc)
{
    std::cout << "Callback overload" << std::endl;
}

void myFunc(const std::string &separator)
{
    std::cout << "std::string overload" << std::endl;
}

bool testCallback(char)
{
    return true;
}

int main()
{
    myFunc("String literal");
    myFunc(std::string("std::string"));
    myFunc(testCallback);

    return 0;
}

出力:

std::string overload
std::string overload
Callback overload
于 2013-07-11T06:26:51.523 に答える