Я понимаю, что после typename, одного &
означает ссылку lvalue и двойной &&
означает ссылку на значение или «выведенную ссылку», также называемую универсальная ссылка Скотт Мейерс. Но я никогда не видел тройной &&&
в сигнатуре функции или метода. Что это значит?
Следующий код производит метод с тройным &&&
:
#include <iostream>
#include <utility>
template<typename ostream> void foo(ostream&& out) {
out << "foo" << std::endl;
}
template<typename ostream> void bar(ostream&& out) {
foo(std::forward<ostream>(out));
}
int main() {
bar(std::cout);
}
После компиляции кода с g++-4.8.1
, Я бегу nm -j a.out | c++filt
, -j
Переключатель является (я полагаю) нестандартным, это означает просто отображать имена символов (без значения или типа). Я получаю это:
__GLOBAL__sub_I_triple_ampersand.cpp
void bar<std::ostream&>(std::ostream&&&)
void foo<std::ostream&>(std::ostream&&&)
__static_initialization_and_destruction_0(int, int)
std::ostream::operator<<(std::ostream& (*)(std::ostream&))
std::ios_base::Init::Init()
std::ios_base::Init::~Init()
std::cout
std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
std::ostream&&& std::forward<std::ostream&>(std::remove_reference<std::ostream&>::type&)
std::piecewise_construct
std::__ioinit
std::basic_ostream<char, std::char_traits<char> >& std::operator<<<std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
___cxa_atexit
__mh_execute_header
_main
dyld_stub_binder
Я получаю похожий вывод при компиляции с clang++
, Итак, мой вопрос: что означает тройной амперсанд? Видимо я не могу написать &&&
в моем коде напрямую. Это потому что мой c++filt
неправильно раскладывает символы? Я использую систему при условии c++filt
в Mac OS X 10.8.
Тот факт, что вы называете свой параметр шаблона ostream
и создать его с std::ostream&
сбивает с толку.
Существует небольшая ошибка в раскладывании (она должна была поставить пробел, чтобы разбить группу &
как его смысл & &&
и не && &
), но наличие последовательного &
Сами по себе не проблема: другим способом разоблачить имя было бы void bar<$T=std::ostream$&>($T$&&)
то есть искаженное имя содержит обратную ссылку на параметры, а не расширение самого типа.
Зачем? В общем случае искаженные имена экземпляров должны представлять точный экземплярный шаблон и его параметры (в отличие от использования сигнатуры, возникающей в результате таких вещей, как правило свертывания или расширение ссылок на признаки), в качестве выражения двух разных шаблонов функций с одним и тем же именем, что приводит к в одной и той же сигнатуре может справедливо сосуществовать в программе (я не думаю, что они могут в модуле компиляции, но поиск имени и разрешение перегрузки настолько сложны в C ++, что я могу ошибаться), и использование упрощенной сигнатуры приведет к их неадекватному объединению , И пример с чертами, я в настоящее время не могу думать об одном с правилами свертывания ссылок (*), два следующих шаблона функции могут быть перегружены в программе (даже в том же CU)
template <typename T>
struct Traits{};
template <typename T>
void f(typename Traits<T>::T1)
{
return;
}
template <typename T>
void f(typename Traits<T>::T2)
{
return;
}
Но если у вас есть следующая специализация черт
template <>
struct Traits<int> {
typedef int T1;
typedef int T2;
};
ты не можешь использовать f<int>(42)
в месте, где оба видны, поскольку существует неоднозначность, но, делая их выборочно видимыми, вы можете использовать один в CU, а другой — в другом CU, так что искажение должно иметь возможность изменить ситуацию.
(*) Возможно, что нет ни одного, я не помню, чтобы кто-то упомянул упомянутое, и как только механизм должен быть на месте (другая причина — ограничение размера искаженных имен), это безопаснее и, вероятно, легче продолжать использовать его для всего.
Других решений пока нет …