web-dev-qa-db-ja.com

std :: to_string-オーバーロードされた関数のインスタンスが引数リストと一致する

counterintです

void SentryManager::add(std::string name,std::shared_ptr<Sentry>){
    name = name + std::to_string(counter);
}

このエラーを止める最良の方法は何でしょうか?私が怠けていたとき、intlong long(または何か)、しかしこれを解決するより良い方法があると確信しています。

エラーメッセージ:

sentrymanager.cpp(8): error C2668: 'std::to_string' : ambiguous call to overloaded function

Visual C++ 2010 Expressを使用しています。

52
pighead10

VC++ 2010には、std::to_stringlong long、およびunsigned long longをそれぞれ取るlong doubleの3つのオーバーロードがあります。明らかにintはこれらのどれでもなく、ある変換は別の変換よりも優れている( demo )ため、変換を暗黙的/明確に行うことはできません。

実際のC++ 11サポートに関しては、これはVC++ 2010の標準ライブラリ実装の一部で失敗しています。C++ 11標準自体は、実際にnineオーバーロードstd::to_string([string.conversions]/7):

string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

これらのオーバーロードがすべて存在していれば、明らかにこの問題は発生しません。ただし、VC++ 2010は、実際のC++ 11標準(リリース時にはまだ存在していなかった)に基づいていたのではなく、むしろ N30 (from 2009)、これらの追加のオーバーロードをnot呼び出します。したがって、VC++tooをここで責めるのは厳しい...

いずれにせよ、ほんの一握りの呼び出しに対して、キャストを使用してあいまいさを自分で解決しても問題はありません。

void SentryManager::add(std::string& name, std::shared_ptr<Sentry>) {
    name += std::to_string(static_cast<long long>(counter));
}

または、コードベースでstd::to_stringを頻繁に使用する場合は、いくつかのラッパーを作成して代わりに使用します。この方法では、呼び出しサイトのキャストは不要です。

#include <type_traits>
#include <string>

template<typename T>
inline
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, std::string>::type
to_string(T const val) {
    return std::to_string(static_cast<long long>(val));
}

template<typename T>
inline
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, std::string>::type
to_string(T const val) {
    return std::to_string(static_cast<unsigned long long>(val));
}

template<typename T>
inline typename std::enable_if<std::is_floating_point<T>::value, std::string>::type
to_string(T const val) {
    return std::to_string(static_cast<long double>(val));
}

// ...

void SentryManager::add(std::string& name, std::shared_ptr<Sentry>) {
    name += to_string(counter);
}

上記のSFINAEの使用でVC++ 2010が成功するか失敗するかを確認できません。失敗した場合は、SFINAEの代わりにタグディスパッチを使用して、次のものをコンパイルする必要があります(潜在性が低い場合)。

#include <type_traits>
#include <string>

namespace detail {
    template<typename T>                   // is_float         is_unsigned
    inline std::string to_string(T const val, std::false_type, std::false_type) {
        return std::to_string(static_cast<long long>(val));
    }

    template<typename T>                   // is_float         is_unsigned
    inline std::string to_string(T const val, std::false_type, std::true_type) {
        return std::to_string(static_cast<unsigned long long>(val));
    }

    template<typename T, typename _>       // is_float
    inline std::string to_string(T const val, std::true_type, _) {
        return std::to_string(static_cast<long double>(val));
    }
}

template<typename T>
inline std::string to_string(T const val) {
    return detail::to_string(val, std::is_floating_point<T>(), std::is_unsigned<T>());
}
86
ildjarn

C++ DR 1261 につまずいた

int」は「_long long_」と「_long long unsigned_」の間であいまいなので、コード「int i; to_string(i);」はコンパイルに失敗します。 _to_string_を使用するためだけに、ユーザーがより大きな型に数字をキャストすることを期待するのは不合理なようです。

提案されている解決策は、オーバーロードを追加することです。 GCCはこれを既に実装しています ; MSVCにはないようです。

11
zwol