Может ли компилятор C ++ попробовать разные реализации (шаблон T), пока не найдет компилятор (для T)? - PullRequest
0 голосов
/ 29 октября 2009
// First try this:

template <class T> T Read(istream& in) {
  T t;
  in >> t;
  return t;
}

// If there is no operator>>(istream&, T) try this: 

template <class T> T Read(istream& in) {
  return T (in);
}

// If there is no constructor T(istream&) try this:

template <class T> T Read(istream& in) {
  return T::OfStream (in);
}

// now fail.

Может ли это быть реализовано?

Если нет, каковы альтернативы?

Ответы [ 5 ]

8 голосов
/ 29 октября 2009

Вам знакомо понятие SFINAE ? Используя эту концепцию, вы можете включать или исключать шаблоны функций из набора кандидатов на основе любого свойства аргументов шаблона. Однако, как сказал Алекс Мартелли, это должно происходить в сигнатуре, а не в теле метода.

Это означает, что вы должны иметь возможность принимать решения во время компиляции относительно некоторого свойства типа T и использовать результат этого решения, чтобы заставить подпись шаблона стать недопустимой, что исключит этот шаблон из набора кандидатов компилятора без поднимая ошибку компиляции.

В Boost есть две библиотеки, которые могут это облегчить: Boost.TypeTraits , что позволяет задавать такие вопросы, как "Является ли T массивом?" или "Т указатель?" или "T является подклассом U?" во время компиляции. Результат этого запроса может быть использован Boost.EnableIf для исключения функции (или нет, по желанию).

Возможно, вы сможете достичь того, что вы есть, после использования комбинации этих библиотек. Если вы используете определенный компилятор, вы также можете достичь аналогичных результатов, используя специфичные для компилятора расширения (если это нормально для вас). Например, используя MSVC, вы можете использовать ключевое слово __ if_exists . В зависимости от того, насколько точно ваш простой пример отражает то, что вы действительно хотите сделать, один метод может быть чище другого.

1 голос
/ 29 октября 2009

Как уже упоминалось, это неоднозначно. Вы должны смотреть на повышения enable_if или аналогичные, в сочетании, например, с. is_function.

0 голосов
/ 29 октября 2009

Как показывают другие ответы, он не соответствует стандарту.

Вы не указали предполагаемое использование, поэтому немного сложно определить, что именно вы ищете, но вы можете реализовать код самостоятельно (см. Ниже), который можно улучшить еще больше с помощью SFINAE, чтобы избежать создания шаблонов преобразования для каждого конкретного класса. :

#include <iostream>
#include <sstream>

using namespace std;

struct A
{
    int x;
    A() : x(0) {}
};

istream& operator>>(istream& in, A& a)
{
    in >> a.x;
    return in;
}

ostream& operator<<(ostream& on, A& a) { return on << "A: " << a.x; }

struct B
{
    int x;
    B(istream& in) : x(0) { in >> x; }
};

ostream& operator<<(ostream& on, B& b) { return on << "B: " << b.x; }

struct C
{
    int x;
    C() : x(0) {}

    static C OfStreamX(istream& in)
    {
        C c;
        in >> c.x;
        return c;
    }
};

ostream& operator<<(ostream& on, C& c) { return on << "C: " << c.x; }

template <typename T> T Read(istream& in);
template <> A Read(istream& in)
{
    A a;
    in >> a;
    return a;
}

template <> B Read(istream& in) { return B(in); }
template <> C Read(istream& in) { return C::OfStreamX(in); }

int main()
{
    string data("23 45 67");
    istringstream in(data);

    A a = Read<A>(in);
    cout << a << endl;

    B b = Read<B>(in);
    cout << b << endl;

    C c = Read<C>(in);
    cout << c << endl;
}

Выход:

A: 23
B: 45
C: 67
0 голосов
/ 29 октября 2009

Это не может быть реализовано напрямую, как вы указали, но есть обходной путь. Можно определить оператор преобразования шаблона, и его параметр типа может быть выведен из ожидаемого целевого типа. Таким образом, вы можете ввести прокси-класс:

class read_proxy
{
public:
    read_proxy(std::istream& in) : in(in) {}
    template<class T> operator T () { T x; in >> x; return x; }
private:
    std::istream& in;
};

read_proxy read(std::istream& in)
{
    return read_proxy(in);
}

А затем используйте его так, как вы изначально хотели:

void foo(float) {}

int main()
{
    int x = read(std::cin);
    foo(read(std::cin)); // float
}

Потенциальная проблема здесь в том, что если кто-то попытается сохранить возвращенный прокси-сервер сам, у него могут возникнуть проблемы с временем жизни (поскольку он содержит простую ссылку на поток, и последний может быть уничтожен до того, как прокси-сервер активен).

0 голосов
/ 29 октября 2009

В стандартной реализации C ++ эти несколько неоднозначных шаблонов должны выдавать ошибку (неоднозначность определяется сигнатурой, а не телом). Я не знаю ни одного компилятора C ++, который нарушает стандарт в той степени, что допускает этот код (это не значит, что не существует достаточно сумасшедших компиляторов, просто я не слышал ни о каких из них ;-).

...