Этот прискорбно длинный пример является урезанной версией реального фрагмента кода, который я пытаюсь написать. У него две проблемы. Во-первых, как написано, он не компилируется: ошибка
error: cannot bind ‘std::string’ lvalue to ‘std::string&&’
error: initializing argument 3 of ‘void pack_arg(datum*, size_t, std::string&&)’
и вызывается наличием вызова к shut_the_door
, Если я достану &&
в соответствующей перегрузке pack_arg
, затем компилируется, но сгенерированный код, по-видимому, копирует обе строки без необходимости. Во-вторых, мне нужно убедиться, что все временные строки, что массив d
держит указатели, оставайся в живых до vprocess
, Прямо сейчас сгенерированный код, кажется, разрушает их прямо перед этим вызовом.
Я готов рассмотреть довольно радикальные изменения в том, как массив заполняется, если у меня есть возможность запустить каждый аргумент process
через перегруженный вызов функции, независимый от всех других подобных аргументов. (В частности, я подозреваю, что из-за специальных правил для временных инициализаций ссылки проблема 2 исчезнет, если я смогу использовать std::string&
в союзе, но как каждый член datum
Массив заполняется присваиванием, а не инициализацией, что в настоящее время не будет работать, и я не знаю, как его заполнить инициализацией, не теряя при этом вызовы pack_arg
, которые необходимы в более широком контексте.)
РЕДАКТИРОВАТЬ: С помощью std::forward
между pack_args
а также pack_arg
не похоже на помощь; Я получаю точно такие же сообщения об ошибках.
#include <string>
#include <stddef.h>
using std::string;
union datum
{
const string* s;
int i;
};
inline void
pack_arg(datum* d, size_t n, int t)
{
d[n].i = t;
}
inline void
pack_arg(datum* d, size_t n, string && t)
{
d[n].s = &t;
}
inline void
pack_args(datum*, size_t)
{
}
template <typename X, typename... XS> inline void
pack_args(datum* d, ::size_t n, X&& x, XS&&... xs)
{
pack_arg(d, n, x);
pack_args(d, n+1, xs...);
}
extern void vprocess(datum*, size_t);
template <typename... XS> inline void
process(XS&&... xs)
{
size_t n = sizeof...(xs);
datum d[n];
pack_args(d, 0, xs...);
vprocess(d, n);
}
extern string shut_the_door();
void
demo()
{
process(1, 2, "buckle my shoe", 3, 4, shut_the_door());
}
Люк приносит действительно интересное замечание (я кратко просмотрел код, читая только подписи):
Если вы собираетесь хранить указатели на std::string
в контейнере, то ваш код должен убедитесь, что аргументы не rvalues, так как после завершения вызова функции оставит висячий указатель.
Чем больше я думаю о вашем коде, тем больше я верю, что все это просто плохая идея. Без тега о том, что представляет собой каждый элемент, вы не сможете узнать после того, какой элемент массива какого типа. Время жизни объектов будет трудно поддерживать (для std::string
в частности …) Может быть, вы должны переосмыслить дизайн в целом.
Вы столкнулись с интересной особенностью нового синтаксиса C ++ 11. Двойной амперсанд (&&
) в этих двух декларациях на самом деле не одно и то же:
inline void
pack_arg(datum* d, size_t n, string && t); // [1]
template <typename X, typename... XS> inline void
pack_args(datum* d, ::size_t n, X&& x, XS&&... xs) // [2]
В [1] аргумент типа string&&
будет привязываться только к r-значению, позволяющему различать временный (или std::move
d объект) из lvalue. Rvalue ссылка ведет себя как lvalue внутри функции. В [2], потому что вы находитесь в шаблоне и X
является логическим типом, применяется другой набор правил и X&&
будет преобразован либо в ссылку lvalue, либо в ссылку rvalue, в зависимости от аргумента.
В вашем случае аргумент на самом деле является временным, поэтому X&&
будет сопоставлен с std::string&&
, но внутри функции она рассматривается как lvalue и не может быть связана. Вы можете исправить это, добавив std::forward
:
template <typename X, typename... XS> inline void
pack_args(datum* d, ::size_t n, X&& x, XS&&... xs)
{
pack_arg(d, n, std::forward(x));
pack_args(d, n+1, std::forward(xs)...);
}
std::forward
Шаблон создаст ссылку lvalue или rvalue в зависимости от того, является ли аргумент самой ссылкой lvalue или rvalue.
Других решений пока нет …