2

所以我有一个结构 X:

struct X
{
    int typecode;
    char* pData;
    int length;
    ...
}

和一长串类型,我们称这个集合为 TS。TS 包括大多数原始类型和几种类类型。

对于 TS 中的每种类型 T,我定义了一个常规函数:

void setup(X& x, const T& t);

例如对于 T = 字符串设置如下所示:

void setup(X& x, const string& s)
{
    x.typecode = X_STRING;
    x.pData = s.c_str();
    x.length = s.size();
    ...
}

现在我有一个模板函数 convert_to_x:

template<class T>
X convert_to_x(const T& t)
{
    X x;
    memset(x, 0, sizeof(x));
    setup(x, t);
    return x;
}

还有一个函数 f,它接受一个 X 数组:

void f(X* xs, int num_args);

还有一个可变参数模板函数g

template<class... Args)
void g(Args... args)
{
    constexpr num_args = sizeof...(args);

    X xs[] = { convert_to_x(args)... };

    f(xs, num_args);
}

发生的事情是,您可以使用任意数量的参数和类型调用 g,它将参数转换为 X 类型的数组,然后调用 f。

问题是,如果 g 被调用的类型不在 TS 中,但可以转换为 TS 中的类型,则会发生以下情况:

  1. 调用转换构造函数来创建临时 t。
  2. setup将存储一个指向这个临时的指针。
  3. 临时的被破坏了。
  4. f 使用包含悬挂指针的 xs 调用。

我需要一种方法g来转换任何可转换为 TS 中的类型但不是 TS 中的类型的参数,并将它们保留在g.

实现这一目标的最佳方法是什么?

更新:

我刚刚想到的一种可能可行的方法是为convertTS 中的每种类型 T 定义一个常规函数,如下所示:

T convert(const T& t) { return t; }

然后为 g 定义一个包装器:

template<class... Args>
void g2(Args... args)
{
    g(convert(args)...);
}

但我认为这会导致不必要地复制已经在 TS 中且不需要转换的类型。有没有办法使用右值/左值语义来避免这种情况?

更新 2:

也许这会起作用:

对于 TS 中的每个 T:

const T& convert(const T& t) { return t; }
T convert(const T&& t) { return t; }

然后:

template<class... Args>
void g2(Args... args)
{
    g(convert(args)...);
}

是否有任何情况下安装程序可能会收到上述临时文件?

4

1 回答 1

1

您可以为以下内容添加已删除的重载setup

void setup(X& x, const string& s) = delete;

由于右值引用比 const 左值引用更容易绑定到临时对象,setup因此使用临时对象调用将选择带有右值引用的重载。但是由于这个setup重载被删除,实际调用它是非法的。因此,当g使用错误类型的参数调用时,该行X xs[] = { convert_to_x(args)... };需要convert_to_args调用 的已删除版本setup,因此实例化失败。反过来,它也会导致 的特定实例化g失败。

编辑

查看您的更新#2,这应该可以。由于convert已经导致最佳转换为 , 之一TSg因此永远不应使用不需要的类型调用。因此,在调用setup. 因此,任何临时对象都将成为 的参数g,并保证在 的期间内存活g

编辑 2

你是对的,这T convert(const T&& t) { return t; }可能会导致值被不必要地复制。但这很容易解决:

const T& convert(const T& t) { return t; }
const T&& convert(const T&& t) { return std::move(t); }

你不会得到任何值的复制。临时对象的生命周期也是正确的,因为临时对象一直存在到创建它们的完整表达式的末尾:

template<class... Args>
void g2(Args... args)
{
    g(convert(args)...);
} //          ^^^^ temporaries created by a conversion here will live until g returns
于 2012-08-20T02:00:21.377 回答