簡體   English   中英

包裝遞歸可變參數模板類會更改行為。 為什么?

[英]Wrapping a recursive variadic template class changes behavior. Why?

希望這會激起社區中的一些人的興趣。 希望它不是太明顯,因為我不確定發生了什么。 我創建了帶有遞歸定義的可變參數模板類,主要是作為一個有趣的自我挑戰。 有點像元組一樣,這個類創建unordered_maps的unordered_maps,任意深度,每層都有任意鍵類型。 例如,您可以創建nested_map<int, std::string, float, int> ,然后使用map["fred"][3.4][42] = 35;設置它map["fred"][3.4][42] = 35; 這是代碼 - 不是太瘋狂。

template<typename T, typename K, typename ... KS> struct nested_map_base : std::unordered_map<K, T>
{
  T &operator[](const K &key)
  {
    // just to verify we get to the bottom of things recursively
    std::cout << "base: key = " << key << std::endl;

    return this->std::unordered_map<K, T>::operator[](key);
  }
};

template<typename T, typename New_K, typename K, typename ... KS>
struct nested_map_base<T, New_K, K, KS ...>
: std::unordered_map<New_K, nested_map_base<T, K, KS...>>
{
  nested_map_base<T, K, KS...> &operator[](const New_K &new_key)
  {
    // just for debugging and to demonstrate that it's working
    // for purposes of this question
    std::cout << "midway: key = " << new_key << std::endl;

    return this->std::unordered_map<New_K, nested_map_base<T, K, KS...>>::operator[](new_key);
  }
};

工作正常。 運行以下代碼,得到預期的輸出 -

std::cout << "Method1:" << std::endl << std::endl;
nested_map_base<int, std::string, double, int> test_nest;
std::cout << "insert" << std::endl;
test_nest["leonard"][4.8][45] = 111;
std::cout << "retrieve" << std::endl;
int &answer = test_nest["leonard"][4.8][45];
std::cout << "Aanswer should be 111. Answer is " << answer << std::endl << std::endl;

生產 -

Method1:

insert
midway: key = leonard
midway: key = 4.8
base: key = 45
retrieve
midway: key = leonard
midway: key = 4.8
base: key = 45
Aanswer should be 111. Answer is 111

整齊。 然后我想我想將它包裝在外部類中以保持實現私有,所以我就這樣開始 -

template<typename datum_type, typename ... keys> class nested_map
{
private:
  nested_map_base<datum_type, keys ...> backing_store;

public:
  template<typename Base_key, typename ... KS> auto operator[](const Base_key &key)
  {
    return backing_store[key];
  }
};

沒有什么,並且起初它似乎工作,但以下代碼產生不同的結果 -

std::cout << "Method2:" << std::endl << std::endl;
nested_map<int, std::string, double, int> test_nest;
std::cout << "insert" << std::endl;
test_nest["leonard"][4.8][45] = 111;
std::cout << "retrieve" << std::endl;
int &answer = test_nest["leonard"][4.8][45];
std::cout << "Answer should be 111. Answer is " << answer << std::endl << std::endl;

它產生了這個 -

Method2:

insert
midway: key = leonard
midway: key = 4.8
base: key = 45
retrieve
midway: key = leonard
midway: key = 4.8
base: key = 45
Answer should be 111. Answer is 0

遞歸可變參數模板元編程充滿了陷阱,並且有理由不會經常包裝,所以我並不感到震驚的是包裹的那個不起作用,但讓我感到驚訝的是它怎么沒有工作。 它按預期遞歸,直到包含終端基准類型的std::unordered_map 在調試器中,從終端映射中恢復了對int的引用,並且在簡單測試代碼中將其設置為111。 您第二次看到鍵被遞歸的事實表明檢索過程似乎也在起作用,但引用的是零值int。 好奇。

我正在深入挖掘調試器,以查看,例如,集合引用的實際地址值是否與用於檢索的引用相同。 我認為它們可能不同的唯一方法是,例如,倒數第二個遞歸層返回最后一層的臨時值,而不是引用數據結構中的那個。 或者也許在包裝的情況下,它們都是臨時的而不是參考......類似的東西,但包裝是如此輕,似乎不可能。 所以如果我發現更多內容,我會添加評論,但我想我會把它扔給社區,看看是否有一些不同的眼睛可以通過檢查來挑逗。

在“ 模板參數演繹”的“Cppreference”頁面中有一個關於自動返回功能的部分,該部分描述了當auto用作函數返回時的規則。

模板參數推導是在功能,演繹的含義是,當使用申報auto在函數的說明符return類型,從return的語句。

對於自動返回函數,參數P如下獲得:在T ,包含auto的函數的聲明的返回類型,每次出現的auto都被虛構的模板參數U替換。 參數A是return語句的表達式,如果return語句沒有操作數,則Avoid() 按照上述規則從PA扣除U后,將推導出的U代入T以得到實際的返回類型。

這可以解釋為什么auto&工程和auto沒有。

暫無
暫無

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

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