Конструктор по ссылке и конструктор по значению?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.04.18 16:43
Оценка:
Здравствуйте!

Есть такое:
class A;

class B
{
    B(const A &a);
    B(A a);
};


Компилятор не ругается, но не очень понятно, какой конструктор когда будет вызываться.
Маньяк Робокряк колесит по городу
Re: Конструктор по ссылке и конструктор по значению?
От: reversecode google
Дата: 05.04.18 17:29
Оценка:
он будет ругаться когда начнете использовать
https://ideone.com/8kOcar
Re[2]: Конструктор по ссылке и конструктор по значению?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.04.18 17:48
Оценка:
Здравствуйте, reversecode, Вы писали:


R>он будет ругаться когда начнете использовать

R>https://ideone.com/8kOcar

Хм. А как быть? Конструктор по ссылке не работает, если class A это какой-нибудь int и используется константа, то не работает.


ЗЫ. Проверил — работает
  Скрытый текст
template<typename T>
struct Test
{
    T    t;
    Test(const T &t_) : t(t_) {}
};

Test<int> foo()
{
    return 3;
}


int main() {
    
    auto res = foo();
    //cout<<"foo returns "<<res.t<<"\n";
    
    for(int i=0; i!=res.t; ++i)
    {}
        
    while(true)    
    {}
    return 0;
}


Получается — ложная тревога. Просто коллега на работе сказал, что не работает, вроде довольно опытный, я ему поверил, а проверить под вечер было некогда. Завтра буду смотреть, где он накосячил
Маньяк Робокряк колесит по городу
Re[3]: Конструктор по ссылке и конструктор по значению?
От: watchmaker  
Дата: 05.04.18 18:21
Оценка: 23 (3)
Здравствуйте, Marty, Вы писали:


M> Просто коллега на работе сказал, что не работает


Именно сказал, что не работает, или просто посоветовал так не делать?
Просто часто небольшие объекты (bool, int, std::string_view, указатели, и прочее) выгоднее передавать по значению в функции, ибо это помогает компилятору генерировать более быстрый код.
Скажем, если он видит, что функция принимает параметр int i, и сам этот параметр не меняется внутри, то значит этот параметр константа. В случае же, если функция принимает параметр const int& i, то несмотря на наличие const, компилятор не будет считать его константой, например, из-за того, что где-то в другом потоке другой код может сделать const_cast<int&>(i) += 1 над той же самой переменной (а доказать невозможность таких сценариев сложно и не всегда у компилятора получается).
Пример: https://godbolt.org/g/F49ekh — в первой функции компилятор считает, что i не изменяется, а во второй вместо этого перечитывает каждый раз её значение из памяти.
И если для обычных функций легко выбрать нужный тип, то в шаблонах это сделать чуть сложнее.
Можно, конечно, смириться с замедлением и везде передавать как const T&. Но есть вариант написать другой шаблон (например, boost::call_traits<T>::param_type), который будет раскрываться в нужный тип (то есть для int он вернёт int, а для std::set<int> вернёт const std::set<int>&).
Re[4]: Конструктор по ссылке и конструктор по значению?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.04.18 18:59
Оценка:
Здравствуйте, watchmaker, Вы писали:

M>> Просто коллега на работе сказал, что не работает


W>Именно сказал, что не работает, или просто посоветовал так не делать?


Мы обсуждали одну идею, он накидал что-то, и сказал, что конструктор по ссылке не работает

W>Можно, конечно, смириться с замедлением и везде передавать как const T&. Но есть вариант написать другой шаблон (например, boost::call_traits<T>::param_type), который будет раскрываться в нужный тип (то есть для int он вернёт int, а для std::set<int> вернёт const std::set<int>&).


Буста нет, есть C++ 11, поддерживаемый частично. Можно попробовать самому наколбасить.

Но не понял, как бустовый boost::call_traits использовать, не мог бы накидать пример?
Маньяк Робокряк колесит по городу
Re[4]: Конструктор по ссылке и конструктор по значению?
От: Constructor  
Дата: 05.04.18 19:15
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Можно, конечно, смириться с замедлением и везде передавать как const T&. Но есть вариант написать другой шаблон (например, boost::call_traits<T>::param_type), который будет раскрываться в нужный тип (то есть для int он вернёт int, а для std::set<int> вернёт const std::set<int>&).


К сожалению, у boost::call_traits<T>::param_type есть довольно неприятный недостаток.
В обычном случае все нормально:
#include <boost/call_traits.hpp>

#include <iostream>


void foo(boost::call_traits<int>::param_type value)
{
    std::cout << "foo(" << value << ")" << std::endl;
}


int main()
{
    foo(42);
}

Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:
template <typename Type>
void templated_foo(typename boost::call_traits<Type>::param_type value)
{
    std::cout << "templated_foo(" << value << ")" << std::endl;
}

// ...

templated_foo(42);

Т.е. в подобных случаях придется явно указывать аргументы шаблона: templated_foo<int>(42);.
Re[5]: Конструктор по ссылке и конструктор по значению?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.04.18 19:19
Оценка:
Здравствуйте, Constructor, Вы писали:


C>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:


У меня скомпилировалось
Маньяк Робокряк колесит по городу
Re[6]: Конструктор по ссылке и конструктор по значению?
От: Constructor  
Дата: 05.04.18 19:26
Оценка:
Здравствуйте, Marty, Вы писали:

C>>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:


M>У меня скомпилировалось


У Вас здесь шаблон класса, аргумент которого известен на момент вызова метода класса. Тут нечему выводиться.
Re[7]: Конструктор по ссылке и конструктор по значению?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.04.18 19:31
Оценка:
Здравствуйте, Constructor, Вы писали:

C>>>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:


M>>У меня скомпилировалось


C>У Вас здесь шаблон класса, аргумент которого известен на момент вызова метода класса. Тут нечему выводиться.


А, ну да. Но мне достаточно
Маньяк Робокряк колесит по городу
Re[5]: Конструктор по ссылке и конструктор по значению?
От: watchmaker  
Дата: 05.04.18 20:48
Оценка:
Здравствуйте, Constructor, Вы писали:

C>К сожалению, у boost::call_traits<T>::param_type есть довольно неприятный недостаток.

C>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:
C>Т.е. в подобных случаях придется явно указывать аргументы шаблона: templated_foo<int>(42);.

Ага. Ну наверное под старый С++03 существенно лучше не сделаешь. А в С++11 это уже можно обойти, если будет надо. Это был просто пример известной реализации, я не призываю использовать вариант именно из буста.
Re[6]: Конструктор по ссылке и конструктор по значению?
От: Constructor  
Дата: 05.04.18 20:55
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Ну наверное под старый С++03 существенно лучше не сделаешь. А в С++11 это уже можно обойти, если будет надо.


Каким образом?
Re[7]: Конструктор по ссылке и конструктор по значению?
От: watchmaker  
Дата: 05.04.18 21:13
Оценка:
Здравствуйте, Constructor, Вы писали:

W>>А в С++11 это уже можно обойти, если будет надо.


C>Каким образом?


Например можно написать такой опус:

template <typename Type>
void templated_foo_impl(typename boost::call_traits<Type>::param_type value)
{
    std::cout << "templated_foo(" << value << ")" << std::endl;
}

template <typename Type>
auto templated_foo(Type&& value) {
    using T = /* тут уже известен Type */;
    return templated_foo_impl<T>(std::forward<Type>(value));
}
Отредактировано 05.04.2018 21:14 watchmaker . Предыдущая версия . Еще …
Отредактировано 05.04.2018 21:13 watchmaker . Предыдущая версия .
Re[4]: Конструктор по ссылке и конструктор по значению?
От: T4r4sB Россия  
Дата: 05.04.18 21:45
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, Marty, Вы писали:



M>> Просто коллега на работе сказал, что не работает


W>Именно сказал, что не работает, или просто посоветовал так не делать?

W>Просто часто небольшие объекты (bool, int, std::string_view, указатели, и прочее) выгоднее передавать по значению в функции, ибо это помогает компилятору генерировать более быстрый код.
W>Скажем, если он видит, что функция принимает параметр int i, и сам этот параметр не меняется внутри, то значит этот параметр константа. В случае же, если функция принимает параметр const int& i, то несмотря на наличие const, компилятор не будет считать его константой, например, из-за того, что где-то в другом потоке другой код может сделать const_cast<int&>(i) += 1 над той же самой переменной (а доказать невозможность таких сценариев сложно и не всегда у компилятора получается).
W>Пример: https://godbolt.org/g/F49ekh — в первой функции компилятор считает, что i не изменяется, а во второй вместо этого перечитывает каждый раз её значение из памяти.

Вот это жопа. Я не знал, думал, что const int& умный компилятор всегда правильно переделывает.
Re[8]: Конструктор по ссылке и конструктор по значению?
От: Constructor  
Дата: 06.04.18 05:51
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>>>А в С++11 это уже можно обойти, если будет надо.


C>>Каким образом?


W>Например можно написать такой опус:[ccode]


Борьба шла за то, чтобы по возможности избавиться от ссылочных параметров, не так ли?
Однако в Вашем опусе вводится дополнительный слой, который в 100% случаев работает со ссылками.
Re[5]: Конструктор по ссылке и конструктор по значению?
От: σ  
Дата: 06.04.18 07:39
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Здравствуйте, watchmaker, Вы писали:


W>>Здравствуйте, Marty, Вы писали:



M>>> Просто коллега на работе сказал, что не работает


W>>Именно сказал, что не работает, или просто посоветовал так не делать?

W>>Просто часто небольшие объекты (bool, int, std::string_view, указатели, и прочее) выгоднее передавать по значению в функции, ибо это помогает компилятору генерировать более быстрый код.
W>>Скажем, если он видит, что функция принимает параметр int i, и сам этот параметр не меняется внутри, то значит этот параметр константа. В случае же, если функция принимает параметр const int& i, то несмотря на наличие const, компилятор не будет считать его константой, например, из-за того, что где-то в другом потоке другой код может сделать const_cast<int&>(i) += 1 над той же самой переменной (а доказать невозможность таких сценариев сложно и не всегда у компилятора получается).
W>>Пример: https://godbolt.org/g/F49ekh — в первой функции компилятор считает, что i не изменяется, а во второй вместо этого перечитывает каждый раз её значение из памяти.

TB>Вот это жопа. Я не знал, думал, что const int& умный компилятор всегда правильно переделывает.


const в одном месте может быть не const в другом и умный компилятор это понимает.
Re[4]: Конструктор по ссылке и конструктор по значению?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 06.04.18 09:13
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Просто часто небольшие объекты (bool, int, std::string_view, указатели, и прочее) выгоднее передавать по значению в функции, ибо это помогает компилятору генерировать более быстрый код.

А для copy-and-swap эта оптимизация будет работать?
Sic luceat lux!
Re[9]: Конструктор по ссылке и конструктор по значению?
От: watchmaker  
Дата: 06.04.18 12:40
Оценка:
Здравствуйте, Constructor, Вы писали:

C>Здравствуйте, watchmaker, Вы писали:


W>>Например можно написать такой опус:


C>Борьба шла за то, чтобы по возможности избавиться от ссылочных параметров, не так ли?

Борьба идёт за то, чтобы программы работали быстрее. Ну а избавление от ссылочных параметров в тактически важных местах как раз помогает в этой борьбе.

C>Однако в Вашем опусе вводится дополнительный слой, который в 100% случаев работает со ссылками.

Так в этом ничего плохого и нет. Важно ведь не наличие или отсутствие ссылки, а сможет или нет компилятор отследить её жизненный путь — именно это влияет на возможность более агрессивной оптимизации. Копирование int тут помогает именно тем, что у скопированного объекта компилятор видит всю жизнь целиком, а значит может, например, доказать, что он не изменяется. И создание ещё одной ссылки на объект до копирования никак этому не помешает.

Ну да, тут просто используется знание, что у любого современного компилятора в таком коде хватит сообразительности проследить всю жизнь временной ссылки. И это действительно не сложно: она ведь создаётся и тут же исчезает в пределах тривиальной функции.
Re[6]: Конструктор по ссылке и конструктор по значению?
От: watchmaker  
Дата: 06.04.18 12:54
Оценка:
Здравствуйте, σ, Вы писали:

σ>const в одном месте может быть не const в другом и умный компилятор это понимает.


Ага. Но это просто С++ язык такой, в котором const означает не то, что переменная является истинной константой, а то, что её значение запрещено текущему коду напрямую менять.

Было бы, конечно, здорово таки принять в стандарт restrict type qualifier (или анолог), который уже есть давным-давно в языке С, и который по какому-то недоразумению не попал сразу по наследству в С++.
Re: Конструктор по ссылке и конструктор по значению?
От: MasterZiv СССР  
Дата: 09.04.18 07:53
Оценка:
Здравствуйте, Marty, Вы писали:

M> B(const A &a); (1)

M> B(A a); (2)

M>Компилятор не ругается, но не очень понятно, какой конструктор когда будет вызываться.


Один константный, другой неконстантный. Соответственно, если у тебя
в выражении A константный (или rvalue более широко), то у тебя МОЖЕТ вызваться
только первый конструктор.
Если у тебя lvalue, то может быть вызван как тот, так и другой конструктор.
(при условии что у A есть конструктор копирования)
И скорее всего в таком случае будет неоднозначность в разрешении вызываемой
функции и ошибка компиляции.
Re[2]: Конструктор по ссылке и конструктор по значению?
От: σ  
Дата: 09.04.18 09:40
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Здравствуйте, Marty, Вы писали:


M>> B(const A &a); (1)

M>> B(A a); (2)

M>>Компилятор не ругается, но не очень понятно, какой конструктор когда будет вызываться.


MZ>Один константный, другой неконстантный. Соответственно, если у тебя

MZ>в выражении A константный (или rvalue более широко), то у тебя МОЖЕТ вызваться
MZ>только первый конструктор.
Константный:
const A a;
B b{a};

error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
     B b{a};
          ^
prog.cc:5:5: note: candidate: 'B::B(A)'
     B(A) {}
     ^
prog.cc:4:5: note: candidate: 'B::B(const A&)'
     B(const A&) {}
     ^

rvalue:
B b{A{}};

error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
     B b{A{}};
            ^
prog.cc:5:5: note: candidate: 'B::B(A)'
     B(A) {}
     ^
prog.cc:4:5: note: candidate: 'B::B(const A&)'
     B(const A&) {}
     ^


MZ>A константный (или rvalue более широко)

Что значит "более широко"?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.