Перегрузка функции в C++ передача аргументов по значению или по ссылке

Если у нас есть этот пример кода функций в C++

void foo(int x)  { std::cout << "foo(int)"   << std::endl; }
void foo(int& x) { std::cout << "foo(int &)" << std::endl; }

можно ли различать, какую функцию вызывать, делая какие-либо изменения в вызывающих аргументах?

Если функция foo вызывается одним из следующих способов:

foo( 10);

i = 10;
foo( static_cast<const int>(i));

foo( static_cast<const int&>(i)); 

Это называется первой функцией Foo overloaded, потому что она не может передать по ссылке аргумент const к параметру non-const. Но, как бы вы сделали, чтобы вызвать вторую функцию перегрузки foo? Если я рядом ... путь:

int i = 10;
foo( i);

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

в этой ссылке https://stackoverflow.com/a/5465379/6717386 это объясняется одним из способов его решения: использование объектов вместо встроенных типов и выполнение частного конструктора копирования, поэтому он не может сделать копию значения объекта, и его нужно назвать второй функцией перегрузки foo и передать объект по ссылке. Но, есть ли какой-либо способ со встроенным типы? Я должен изменить имя функции, чтобы избежать перегрузки?

5 ответов


вы можете выполнить приведение (функции), чтобы выбрать функцию перегрузки:

static_cast<void (&)(int&)>(foo)(i);

демо


в большинстве случаев перегрузка функций включает различные типы параметров и различные длины входных параметров.

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

вы можете считать просто добавление второго параметра ко второму способу, что-то вроде этого:

void foo(int x)  { std::cout << "foo(int)"   << std::endl; }
void foo(int& x, ...) { std::cout << "foo(int &, ...)" << std::endl; }

здесь ... может быть логическим типом, скажем: bool anotherFunction

и foo(param1, param2) просто вызовет второй код, и все в порядке.


очень странный дизайн, но если вы хотите... Я предложу такое же странное решение, как и ваш дизайнXreference в сигнатуру функции. Затем в функции вы можете проверить, что вам нужно сделать, используя std::is_lvalue_reference, std::is_rvalue_reference.
Что-то вроде этого!--7-->

template<class T>
void foo(T&& x)
{
  static_assert(std::is_same<std::decay_t<T>, int>::value, "!"); 
  if (std::is_rvalue_reference<T&&>::value)
    std::cout << "do here what you want in foo(int x)";
  else
    std::cout << "do here what you want in foo(int & x)";
}

int main()
{
  int x = 5;
  foo(x); //"do here what you want in foo(int x)" - will be printed
  foo(std::move(x)); //"do here what you want in foo(int & x)" - will be printed
}

несмотря на хороший ответ @Jarod42, в качестве альтернативного решения вы можете положиться на шаблонную точку входа и перегрузку внутренней функции (если вы не хотите иметь дело с явными приведениями, конечно).
Это следует за минимальным, рабочим примером:

#include<type_traits>
#include<iostream>
#include<utility>

void foo_i(char, int x)  { std::cout << "foo(int)"   << std::endl; }
void foo_i(int, int &x) { std::cout << "foo(int &)" << std::endl; }

template<typename T>
void foo(T &&t) {
    static_assert(std::is_same<std::decay_t<T>, int>::value, "!");
    foo_i(0, std::forward<T>(t));
}

int main() {
    foo( 10);
    int i = 10;
    foo( static_cast<const int>(i));
    foo( static_cast<const int &>(i)); 
    foo(i);
}

на static_assert служит для проверки того, чтобы параметр был чем-то, что включает int (то есть int, int &, const int &, int &&`, и так далее).

как вы можете видеть из кода выше, foo(i) выведет:

foo (int &)

как и ожидалось.


другой:

#include <iostream>
#include <functional>

void foo(int x)
{
    std::cout << "foo(int)\n";
}

template<typename T>
void foo(T&& x)
{
    std::cout << "foo(int&)\n";
}

int main()
{
    int i = 10;
    foo(i);           // foo(int)
    foo(std::ref(i)); // foo(int&)
}