C ++ - передача по ссылке / значению с использованием шаблонов - PullRequest
0 голосов
/ 13 марта 2020

Контекст

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

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

Но для интерфейсов нескольких классов создание Функция передачи по ссылке для каждого интерфейса может быть… неопрятной, например:

void parse(Console&);
void parse(Dialog&);
void parse(Image&);
void parse(Window&);
void parse(...); // how many more overloads?!

Итак, к шаблонам для решения этой неопрятной проблемы, верно?

template <typename type> void parse(type&);

Но я ударился о дорожную кочку…

Попытка решения (код)

#include <iostream>

class Object { public:
    // Copy/ Move constructors implicitly defined;
    //   Same goes for the assignment operator.
    constexpr inline Object(void) {}

    // Test if the object can be a reference or is an expression value.
    template <typename type>
    inline static void parse(type) noexcept { std::cout << "[pass-by-value]" << std::endl; }

    template <typename type>
    inline static void parse(type&) noexcept { std::cout << "[pass-by-reference]" << std::endl; }
};

Передача объекта по значению работает

Object::parse(Object()); // SUCCESS: "pass-by-value"

, но использование ссылки приводит к конфликту функции теста с ее перегрузками.

Object object {};
Object::parse(object); // ERROR: Ambiguous call of `Object::parse` function.
                       // EXPECTED: "pass-by-reference"

Я предполагаю, что вызов неоднозначен, потому что Перегрузка parse(type) инициализируется с помощью {type=Object&} и Перегрузка parse(type&) инициализируется с помощью {type=Object}.

Хотя я думал, что перегрузка parse(type&) предпочтительнее, но, похоже, это не так.

Итак, что мне здесь не хватает?

Вопрос

Как мы можем различить аргумент, который может быть ссылкой, или аргумент, который является константным выражением, использующим функцию шаблона?

Примечание: я пытаюсь предотвратить ненужные копии с пользовательскими объектами, например:

Если я передам Image() [type=Image] в качестве аргумента, он будет передан по значению. В противном случае он будет передан по ссылке, например:

Image image; // -> image [type=Image&]
Image images[1]; // -> images[0] [type=Image&]
Image *image_p; // -> *(image_p + 0) [type=Image&]

Ответы [ 2 ]

2 голосов
/ 13 марта 2020

Исходя из вашего комментария, кажется, что вам нужна перегрузка для ссылки на r-значение (чтобы вы могли различить guish, когда был передан временный объект или был передан существующий объект)

void foo(int& x){
    std::cout << x << " was an l-value\n";
}

void foo(int&& x){
    std::cout << x << " was an r-value (possibly a temporary or std::move result)\n";
}

int main(){
    int x = 5;
    foo(x);
    foo(10);
}

Обратите внимание, что вторая версия также не выполняет копирование. Создается временный объект, а затем используется семантика перемещения.


Если вы не хотите изменять исходный объект и не заботитесь о различиях, вы можете просто сделать аргумент ссылкой const

void foo(const int& x){
    std::cout << x << " may or may not have been a temporary "
                      "(it's lifetime is prolonged until end of this function)\n";
}


int main(){
    int x = 5;
    foo(x);
    foo(10);
}

Здесь копии также не нужны.

1 голос
/ 13 марта 2020

Это довольно распространенная проблема.

Вы можете отменить методы в зависимости от размера объекта и от того, легко ли его копировать. Если это не просто копируемое или слишком большое, используйте константную ссылку - в противном случае используйте копирование.

Таким образом, у вас нет двусмысленности для разрешения - просто используйте SFINEA для включения / выключения определенных функций.

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