Как проверить, является ли переменная производной от класса? - PullRequest
8 голосов
/ 06 июня 2019

Я хочу напечатать переменные многих типов. Я создал класс с именем IStringable и некоторые классы, производные от него. В моей функции PrintVariable я хочу проверить, является ли параметр производным от IStringable, и если это так, выведите его.

class IStringable {
public:
    virtual ~IStringable() { }
    virtual std::string ToString() const = 0;
}

class Person : public IStringable {
public:
    Person(const std::string name) : _name(name) { }
    virtual std::string ToString() const { return _name; }
private:
    std::string _name;
}

// This does not work as intended, as I don't know how it could be implemented
template <>
void PrintVariable<IStringable>(const IStringable& var) {
    std::cout << var.ToString() << std::endl;
}

int main() {
    Person p("Foo");
    PrintVariable(p);
}

Пока что я обошел эту проблему, просто взамен std::cout << p.ToString() << std::endl;, но мне интересно, есть ли лучшее решение для этого.

1 Ответ

8 голосов
/ 06 июня 2019

Вам не нужен шаблон:

void PrintVariable(const IStringable& var) {
    std::cout << var.ToString() << '\n';
}

Допустимы только вызовы PrintVariable с объектом, конвертируемым в IStringable:

Person p("Alice");
struct Bob {} b;
PrintVariable(p); // OK
PrintVariable(b); // ill-formed: no conversion from Bob to const IStringable&

Кроме того, вы можете изменить дизайнPrintVariable в качестве оператора:

std::ostream& operator<<(std::ostream& os, IStringable const& rhs)
{
    return os << rhs.ToString();
}

Таким образом, вы можете написать:

Person p("Alice");
std::cout << p << '\n';

Как следует из комментариев, OP ищет способ регистрировать вещи.Минимальная реализация была бы:

#include <string_view>
#include <type_traits>
#include <iostream>

namespace Logger
{
    struct IStringable
    {
        virtual ~IStringable() {}
        virtual std::string ToString() const = 0;
    };

    std::string to_string(IStringable const& v) { return v.ToString(); }

    void log(std::string_view const& sv)
    {
        std::cout << "debug: " << sv << '\n';
    }
    template<class T, std::enable_if_t<!std::is_convertible_v<T, std::string_view>, int> = 0>
    void log(T const& v)
    {
        using std::to_string;
        log(to_string(v));
    }
}

Идея состоит в том, чтобы использовать ADL и SFINAE для вызова либо std::to_string, либо ISrtingable::ToString на объекте для входаи зарегистрируйте полученную строку.

Использование:

class Person : public Logger::IStringable {
public:
    Person(const std::string name) : _name(name) { }
    virtual std::string ToString() const { return _name; }
private:
    std::string _name;
};

int main()
{
    Person p("Alice");
    double d = 0.0;
    const char* c = "Some words";

    Logger::log(p);
    Logger::log(d);
    Logger::log(c);
}

Демонстрация: https://coliru.stacked -crooked.com / a / 77e19e87c9d4780d

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...