Подтвердить что ты не робот

Хороший способ предотвратить выполнение кода С++ 03 субоптимально в С++ 11?

У меня был код С++ 03, который реализовал swap для определенных классов, чтобы быстро сделать std::sort (и другие функции).

К сожалению, для меня std::sort теперь, кажется, использует std::move, что означает, что мой код теперь намного медленнее, чем в С++ 03.

Я знаю, что могу использовать #if __cplusplus >= 201103L для условного определения оператора move-constructor/move-assign, но мне интересно, есть ли лучший способ, который не использует препроцессорные хаки?

(Я бы хотел избегать пропроцессорных хаков, потому что они были бы уродливыми, так как я не только должен был тестировать такие версии компилятора, как _MSC_VER >= 1600, но и потому, что они не будут хорошо работать с такими инструментами, как LZZ, которые не распознают синтаксис перемещения С++ 11, но заставляют меня предварительно обработать код.)

4b9b3361

Ответ 1

Кажется, на самом деле возникает вопрос: как можно реализовать конструктор перемещения и назначение переноса с помощью компиляторов С++ 03?

Простой ответ: они не могут! Тем не менее, простой ответ пренебрегает возможностью создания чего-то совершенно корректного кода С++ 03 и который становится конструктором перемещения и перемещает назначение с помощью компилятора С++ 11. Этот подход должен будет использовать некоторые хакеры препроцессора, но этот бит используется только для создания заголовка, определяющего несколько инструментов, используемых для фактической реализации.

Вот простой заголовочный файл, который с радостью компилируется без каких-либо предупреждений с clang и gcc с включенным или отключенным С++ 11:

// file: movetools.hpp
#ifndef INCLUDED_MOVETOOLS
#define INCLUDED_MOVETOOLS
INCLUDED_MOVETOOLS

namespace mt
{
#if __cplusplus < 201103L
    template <typename T>
    class rvalue_reference {
        T* ptr;
    public:
        rvalue_reference(T& other): ptr(&other) {}
        operator T&() const { return *this->ptr; }
    };
#else
    template <typename T>
    using rvalue_reference = T&&;
#endif

    template <typename T>
    rvalue_reference<T> move(T& obj) {
        return static_cast<rvalue_reference<T> >(obj);
    }
}

#endif

Основная особенность заключается в определении шаблона mt::rvalue_reference<T>, который ведет себя как ссылка rvalue в С++ 03 и фактически является ссылкой rvalue (т.е. a T&&) для С++ 11. Он не будет иметь дело с С++ 03 rvalue-ссылками, но, по крайней мере, позволяет создавать конструкторы перемещения и переводить назначения, не требуя при этом ссылок на rvalue.

Обратите внимание, что mt::move() просто используется, чтобы позже показать, как rvalue_reference<T> можно перемещать даже в С++ 03! Главное, что rvalue_reference<T> - это то, что понимает компилятор С++ 03 или T&&. Для этой вполне разумной нотации необходимо, чтобы компилятор поддерживал шаблоны псевдонимов. Если это не так, можно применить тот же трюк, но с использованием подходящего вложенного типа соответствующего шаблона класса.

Вот пример использования этого заголовка:

#include "movetools.hpp"
#include <iostream>

class foo
{
public:
    foo() { std::cout << "foo::foo()\n"; }
    foo(foo const&) { std::cout << "foo::foo(const&)\n"; }
    foo(mt::rvalue_reference<foo> other) {
        std::cout << "foo::foo(&&)\n";
        this->swap(other);
    }
    ~foo() { std::cout << "foo::~foo()\n"; }
    foo& operator= (foo const& other) {
        std::cout << "foo::operator=(foo const&)\n";
        foo(other).swap(*this);
        return *this;
    }
    foo& operator= (mt::rvalue_reference<foo> other) {
        std::cout << "foo::operator=(foo&&)\n";
        this->swap(other);
        return *this;
    }

    void swap(foo&) {
        std::cout << "foo::swap(foo&)\n";
    }
};

int main()
{
    foo f0;
    foo f1 = f0;
    foo f2 = mt::move(f0);
    f1 = f2;
    f0 = mt::move(f1);
}

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

Ответ 2

Взгляните на Boost.Move. Он имеет эмуляцию перемещения для С++ 03. Может быть, это может помочь, но я не взглянул на детали.