8

Summary: I want to end up with a function that deduces the exact types it was called with and takes (e.g.) a tuple that forwards them (the types of which will be different from the exact types the function was called with).

I'm stuck trying to "know" via deduction the types of the arguments to a given function, whilst simultaneously forwarding them. I think I might be missing something crucial about how this works.

#include <tuple>
#include <string>
#include <functional>

template <typename ...Args>
struct unresolved_linker_to_print_the_type {
   unresolved_linker_to_print_the_type();
};

void f(int,double,void*,std::string&,const char*) {
}

template <typename F, typename ...Args>
void g1(F func, Args&&... args) {
  unresolved_linker_to_print_the_type<Args...>();
  auto tuple = std::forward_as_tuple(args...);
  unresolved_linker_to_print_the_type<decltype(tuple)>();
}

template <typename F, typename T, typename ...Args>
void g2(F func, const T& tuple, Args... args) {
  unresolved_linker_to_print_the_type<Args...>();
  unresolved_linker_to_print_the_type<decltype(tuple)>();
}

int main() {
  int i;
  double d;
  void *ptr;
  std::string str;
  std::string& sref = str;
  const char *cstr = "HI";

  g1(f, i,d,ptr,sref,cstr);
  g2(f, std::forward_as_tuple(i,d,ptr,sref,cstr),  i,d,ptr,sref,cstr);
}

What I'd like to see is a scenario where when my function (e.g. g1 or g2) gets called it knows and can use both the original types - int,double,void*,std::string&,const char* and the forwarded arugments too.

In this instance I don't seem to be able to find this information from within g1 or g2. The (deliberate, to print out the types) linker error shows me in g1 they are:

int&, double&, void*&, std::string&, char const*&
int&, double&, void*&, std::string&, char const*&

and in g2:

int, double, void*, std::string, char const*
int&, double&, void*&, std::string&, char const*&

There are two thing I don't get here:

  1. Why do none of the printed (via the linker error) types match what I actually passed in? (int,double,void*,std::string&,const char). Can I deduce what I actually was passed? Preferably with "natural" syntax, i.e. everything just once and nothing explicitly written out. I can explicitly write:

    g2<decltype(&f),decltype(std::forward_as_tuple(i,d,ptr,sref,cstr)),int,double,void*,std::string&,const char*>(f,std::forward_as_tuple(i,d,ptr,sref,cstr),i,d,ptr,sref,cstr);
    

    but that's "unwieldy" to say the least!

  2. In g1 the presence of && in the function signature declaration seems to alter the types in the template parameter Args itself. Compare that with:

    template <typename T>
    void test(T t);
    

    Or:

    template <typename T>
    void test(T& t);
    

    using either of those with:

    int i;
    test(i);
    

    doesn't change the type of T. Why does the && change the type of T itself when & doesn't?

4

2 回答 2

2

回答第一个问题:

函数的参数是表达式,而不是类型。这两者之间的区别在第 5 章 [expr], p5 中表达:

如果表达式最初具有类型“对 T 的引用”(8.3.2、8.5.3),则在任何进一步分析之前将类型调整为 T。

g(str)因此,和之间没有任何区别g(sref)g()总是看到 a std::string,而不是参考。

此外,表达式可以是左值或右值(实际上这是 C++11 规则的简化,但对于本次讨论来说已经足够接近了——如果您想要它们在 3.10 [basic.lval] 中的详细信息)。

回答第二个问题:

表单模板参数:

template <class T>
void g(T&&);

很特别。它们不同于T, T&, 甚至const T&&在以下方面:

T&&绑定到左值时,T推导为左值引用类型,否则T完全按照正常推导规则推导。

例子:

int i = 0;
g(i);  // calls g<int&>(i)
g(0);  // calls g<int>(0)

这种行为是为了支持所谓的完美转发,通常看起来像:

struct A{};

void bar(const A&);
void bar(A&&);

template <class T>
void foo(T&& t)
{
     bar(static_cast<T&&>(t));  // real code would use std::forward<T> here
}

如果有人调用foo(A())(右值A),则T根据正常规则推断为A。在里面foo我们强制t转换为A&&(一个右值)并调用bar. 然后选择bar采用右值的重载。A即如果我们用右值调用foo,那么用右值调用。foobar

但是,如果我们调用foo(a)(an lvalue A),则T推断为A&。现在演员阵容看起来像:

static_cast<A& &&>(t);

根据参考折叠规则简化为:

static_cast<A&>(t);

即左值t被强制转换为左值(无操作强制转换),因此bar调用采用左值的重载。即如果我们用左值调用foo,那么用左值调用。这就是完美转发一词的来源。foobar

于 2011-10-25T15:42:40.210 回答
0

类型(即使在 C++ 中)主要是编译类型概念(当然除了 vtables 中的 RTTI)。

如果您需要完全动态的类型,那么 C++ 可能不是最好的语言。

您可能会使用插件GCC MELTg++扩展(MELT 是扩展 GCC 的高级域特定语言)来扩展 GCC(实际上,假设它至少是 4.6),它可以满足您的需求(例如,提供一个额外的内置将其参数的类型编码为一些常量字符串等...),但这确实需要一些工作(并且特定于 GCC)。

但我不明白你为什么要在 C 中做这种巴洛克式的事情。如果动态类型对你来说如此重要,你为什么不使用动态类型语言呢?

于 2011-10-28T20:23:32.653 回答