Как преобразовать тип шаблона в строку, если она тоже может быть строкой? - PullRequest
0 голосов
/ 21 января 2019

У меня есть класс:

template <typename val_t>
class tracer_t : public base_tracer_t<val_t> {
    std::vector<std::string> m_trace;
public:
    virtual void push_fact(val_t fact) override {
        std::string str = "+ fact: " + to_string(fact);
        m_trace.push_back(std::move(str));
    }

    virtual void push_rule(const std::string &id, val_t val, bool tg) override {
        std::string str = "+ ";
        if (tg) { str += "target: "; }
        else { str += "rule: "; }
        str += id + " -> " + to_string(val);
        m_trace.push_back(std::move(str));
    }

    virtual void print() override {
        std::cout << "Stack trace: " << std::endl;
        for (auto it = m_trace.begin(); it != m_trace.end(); ++it) {
            std::cout << (*it) << std::endl;
        }
    }
private:
    std::string to_string(val_t val) {
        if (std::is_same<val_t, std::string>::value) {
            return (std::string)val;
        }
        return std::to_string(val);
    }
};

Проблема в том, что он не компилируется, если val_t равен std::string из-за:

tracer.hpp:49: error: no matching function for call to ‘to_string(std::__cxx11::basic_string<char>&)’
         return std::to_string(val);
                ~~~~~~~~~~~~~~^~~~~

Но я не могу понять, как это решить. Я пытался проверить тип вручную, но ошибка во время компиляции, поэтому это не помогло

Ответы [ 2 ]

0 голосов
/ 21 января 2019

Вы можете просто предоставить новую перегрузку для to_string

std::string to_string(const std::string& s) { return s; }

Вы можете поместить приведенный выше код внутри класса, как закрытый метод или в подходящее пространство имен, чтобы избежать возможных конфликтов, когда, скажем, кто-то использует ваш код и хочет написать свою собственную перегрузку to_string .

РЕДАКТИРОВАТЬ: Как отмечено в комментариях ниже, вы не можете поместить такую ​​перегрузку в пространство имен std, так как новое объявление std::to_string запрещено, см. Расширение пространства имен std .

РЕДАКТИРОВАТЬ: Если вам нужно, возможно, позвонить std::to_string, вам может понадобиться добавить дополнительную функцию шаблона to_string в ваш код как

template <typename T>
typename std::enable_if<!std::is_convertible<T, std::string>::value, std::string>::type to_string(T r) const { return std::to_string(r); }

(не забывайте #include <type_traits> за это).

Это потому, что даже если вы импортируете стандартную библиотеку std::to_string с помощью using namespace std, функция-член to_string будет иметь приоритет. Смотрите обсуждение: C ++: почему функция-член имеет приоритет над глобальной функцией . Здесь вы можете увидеть минимальный пример.

0 голосов
/ 21 января 2019

Если вы не хотите специализировать весь класс для std::string, вы можете использовать std::enable_if или if constexpr (c ++ 17)

auto to_string(val_t val)
    -> typename std::enable_if<std::is_same<val_t, std::string>::value, std::string>::type 
{
    return static_cast<std::string>(val);
}

auto to_string(val_t val)
    -> typename std::enable_if<!std::is_same<val_t, std::string>::value, std::string>::type 
{
    return std::to_string(val);
}

или более современный подход сif constexpr

auto to_string(val_t val)
{
    if constexpr (std::is_same<val_t, std::string>::value)
    {
        return static_cast<std::string>(val);
    }
    else
    {
        return std::to_string(val);
    }
}
...