Выводит знание оригинальных типов, одновременно отправляя - PullRequest
8 голосов
/ 25 октября 2011

Резюме: я хочу закончить с функцией, которая выводит точные типы, с которыми она была вызвана, и принимает (например) кортеж, который передает их (типы которых будут отличаться от точных типовфункция была вызвана с помощью.)

Я застрял, пытаясь «узнать» через дедукцию типы аргументов для данной функции, одновременно передавая их.Я думаю, что я мог бы упустить что-то решающее в том, как это работает.

#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);
}

Что я хотел бы увидеть, так это сценарий, когда моя функция (например, g1 или g2) получает вызови может использовать оба исходных типа - int,double,void*,std::string&,const char* и также переадресованные аругменты.

В этом случае мне кажется, что я не могу найти эту информацию из g1 илиg2.Ошибка компоновщика (преднамеренное, чтобы распечатать типы) показывает мне в g1 они:

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

и в g2:

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

Есть две вещи, которые яне получить здесь:

  1. Почему ни один из напечатанных (через ошибку компоновщика) типов не соответствует тому, что я фактически передал?(int,double,void*,std::string&,const char).Могу ли я сделать вывод, что я на самом деле был передан?Желательно с «естественным» синтаксисом, т.е. все только один раз и ничего явно не выписано.Я могу явно написать:

    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);
    

    , но это, по меньшей мере, "громоздко"!

  2. В g1 наличие && в сигнатуре функцииКажется, объявление меняет типы в параметре шаблона Args.Сравните это с:

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

    Или:

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

    , используя любой из них с:

    int i;
    test(i);
    

    не меняет тип T.Почему && меняет тип T, а & нет?

Ответы [ 2 ]

2 голосов
/ 25 октября 2011

Ответ на первый вопрос:

Аргументами функций являются выражения , а не типы .Разница между этими двумя выражается в главе 5 [expr], p5:

Если выражение изначально имеет тип «ссылка на T» (8.3.2, 8.5.3), типперед дальнейшим анализом с поправкой на T.

Таким образом, нет никакой разницы между g(str) и g(sref).g() всегда видит std::string, а не ссылку.

Кроме того, выражения могут быть lvalue или rvalue (на самом деле это упрощение правил C ++ 11, но оно достаточно близко для этого обсуждения -если вам нужны подробности, они указаны в 3.10 [basic.lval]).

Ответ на второй вопрос:

Параметры шаблона формы:

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

areспециальный.Они отличаются от T, T& или даже const T&& следующим образом:

Когда T&& связывается с lvalue, T выводится как ссылочный тип lvalue, в противном случае 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 со значением r, тогда foo вызывает bar со значением r.

Но если мы вызываем foo(a) (значение A), то T выводит какA&.Теперь приведение выглядит следующим образом:

static_cast<A& &&>(t);

, что в соответствии с правилами свертывания ссылок упрощается до:

static_cast<A&>(t);

Т.е. значение lvalue t приводится к значению lvalue (приведение типа no-op.), и, таким образом, перегрузка bar, принимающая lvalue, называется.Т.е. если мы вызываем foo со значением lvalue, то foo вызывает bar со значением lvalue.И вот откуда взялся термин совершенная пересылка .

0 голосов
/ 29 октября 2011

типов (даже в C ++) в основном представляют собой понятие типа компиляции (за исключением, конечно, RTTI в vtables).

Если вам нужны полностью динамические типы, то C ++ может быть не лучшим языком для этого.

Возможно, вы могли бы расширить GCC (на самом деле g++, предполагая, что это не менее 4,6) с помощью плагина или GCC MELT (MELT - это высокоуровневый язык, специфичный для домена расширять GCC), который делает то, что вы хотите (например, предоставляя дополнительную встроенную функцию, которая кодирует тип его аргументов в некоторой константной строке и т. д.), но это требует некоторой работы (и специфично для GCC).

Но я не понимаю, почему вы хотите делать такие вещи в стиле барокко на C. Если динамическая типизация так важна для вас, почему бы вам не использовать язык с динамической типизацией ??

...