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;
}
Получается — ложная тревога. Просто коллега на работе сказал, что не работает, вроде довольно опытный, я ему поверил, а проверить под вечер было некогда. Завтра буду смотреть, где он накосячил
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]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, 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 использовать, не мог бы накидать пример?
Здравствуйте, 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 есть довольно неприятный недостаток.
В обычном случае все нормально:
Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:
C>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:
Здравствуйте, Marty, Вы писали:
C>>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:
M>У меня скомпилировалось
У Вас здесь шаблон класса, аргумент которого известен на момент вызова метода класса. Тут нечему выводиться.
Re[7]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, Constructor, Вы писали:
C>>>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента:
M>>У меня скомпилировалось
C>У Вас здесь шаблон класса, аргумент которого известен на момент вызова метода класса. Тут нечему выводиться.
Здравствуйте, Constructor, Вы писали:
C>К сожалению, у boost::call_traits<T>::param_type есть довольно неприятный недостаток. C>Однако при попытке добавить функции foo шаблонности код не скомпилируется, так как аргумент шаблона не получится вывести из типа передаваемого в функцию аргумента: C>Т.е. в подобных случаях придется явно указывать аргументы шаблона: templated_foo<int>(42);.
Ага. Ну наверное под старый С++03 существенно лучше не сделаешь. А в С++11 это уже можно обойти, если будет надо. Это был просто пример известной реализации, я не призываю использовать вариант именно из буста.
Re[6]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, 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));
}
Здравствуйте, 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]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, watchmaker, Вы писали:
W>>>А в С++11 это уже можно обойти, если будет надо.
C>>Каким образом?
W>Например можно написать такой опус:[ccode]
Борьба шла за то, чтобы по возможности избавиться от ссылочных параметров, не так ли?
Однако в Вашем опусе вводится дополнительный слой, который в 100% случаев работает со ссылками.
Re[5]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, 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]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, watchmaker, Вы писали:
W>Просто часто небольшие объекты (bool, int, std::string_view, указатели, и прочее) выгоднее передавать по значению в функции, ибо это помогает компилятору генерировать более быстрый код.
А для copy-and-swap эта оптимизация будет работать?
Sic luceat lux!
Re[9]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, Constructor, Вы писали:
C>Здравствуйте, watchmaker, Вы писали:
W>>Например можно написать такой опус:
C>Борьба шла за то, чтобы по возможности избавиться от ссылочных параметров, не так ли?
Борьба идёт за то, чтобы программы работали быстрее. Ну а избавление от ссылочных параметров в тактически важных местах как раз помогает в этой борьбе.
C>Однако в Вашем опусе вводится дополнительный слой, который в 100% случаев работает со ссылками.
Так в этом ничего плохого и нет. Важно ведь не наличие или отсутствие ссылки, а сможет или нет компилятор отследить её жизненный путь — именно это влияет на возможность более агрессивной оптимизации. Копирование int тут помогает именно тем, что у скопированного объекта компилятор видит всю жизнь целиком, а значит может, например, доказать, что он не изменяется. И создание ещё одной ссылки на объект до копирования никак этому не помешает.
Ну да, тут просто используется знание, что у любого современного компилятора в таком коде хватит сообразительности проследить всю жизнь временной ссылки. И это действительно не сложно: она ведь создаётся и тут же исчезает в пределах тривиальной функции.
Re[6]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, σ, Вы писали:
σ>const в одном месте может быть не const в другом и умный компилятор это понимает.
Ага. Но это просто С++ язык такой, в котором const означает не то, что переменная является истинной константой, а то, что её значение запрещено текущему коду напрямую менять.
Было бы, конечно, здорово таки принять в стандарт restrict type qualifier (или анолог), который уже есть давным-давно в языке С, и который по какому-то недоразумению не попал сразу по наследству в С++.
Re: Конструктор по ссылке и конструктор по значению?
Здравствуйте, Marty, Вы писали:
M> B(const A &a); (1) M> B(A a); (2)
M>Компилятор не ругается, но не очень понятно, какой конструктор когда будет вызываться.
Один константный, другой неконстантный. Соответственно, если у тебя
в выражении A константный (или rvalue более широко), то у тебя МОЖЕТ вызваться
только первый конструктор.
Если у тебя lvalue, то может быть вызван как тот, так и другой конструктор.
(при условии что у A есть конструктор копирования)
И скорее всего в таком случае будет неоднозначность в разрешении вызываемой
функции и ошибка компиляции.
Re[2]: Конструктор по ссылке и конструктор по значению?
Здравствуйте, 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 более широко)
Что значит "более широко"?