簡體   English   中英

推論原始類型的知識,同時轉發

[英]Deducing knowledge of original types, whilst simultaneously forwarding

簡介:我想以一個推斷出確切類型的函數作為結束,並采用(例如)一個將其轉發的元組(其類型將不同於調用該函數的確切類型)。

我一直試圖通過推導給定函數的參數類型來“知道”,同時轉發它們。 我想我可能缺少有關此工作原理的關鍵信息。

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

我想看到的是一種情況,當我的函數(例如g1g2 )被調用時,它知道並且可以使用兩種原始類型int,double,void*,std::string&,const char* 已轉發也是。

在這種情況下,我似乎無法從g1g2找到此信息。 (故意打印出類型)鏈接器錯誤在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本身的類型?

回答第一個問題:

函數的參數是表達式 ,而不是類型 兩者之間的區別在第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&&);

很特別 它們在以下方面不同於TT&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&&轉換為A&& (一個右值)並調用bar 的過載bar ,它接受一個右值A然后選擇。 即,如果我們用右值調用foo ,則foo用右值調用bar

但是,如果我們調用foo(a) (一個左值A ),則T推導為A& 現在,演員表看起來像:

static_cast<A& &&>(t);

根據該折疊規則,該規則簡化為:

static_cast<A&>(t);

即,將左值t強制轉換為左值(無操作強制轉換),因此將調用采用左值的bar過載。 即,如果我們用左值調用foo ,則foo用左值調用bar 這就是術語“ 完美轉發”的來歷。

類型(甚至在C ++中)大多是編譯類型概念(當然,vtable中的RTTI除外)。

如果您需要完全動態的類型,那么C ++可能不是最佳的語言。

您可能會使用您想要的功能(例如提供額外的內置功能)來擴展GCC(實際上是g++ ,假設至少為4.6),或者使用插件GCC MELT擴展(MELT是擴展GCC的高級領域特定語言)。可以使用常量字符串等方式編碼其參數的類型),但這確實需要做一些工作(並且特定於GCC)。

但是我不明白為什么要在C語言中做這種巴洛克式的事情。如果動態類型對您如此重要,那么為什么不使用動態類型的語言呢?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM