簡體   English   中英

從可變模板類推導出類型包並聲明相同類型包的參數

[英]Deduce types pack from a variadic-templated class and declare an argument of the same types pack

首先,很抱歉問題標題不清楚,如果您想出更好的表述方式,請隨時進行編輯。

我有一堂課:

template <typename ...Arguments>
class CSignal
{
    template <typename ...ActualArguments>
    void invoke(ActualArguments&&... args) const {}
};

還有一個,這就是我遇到的問題:

class SomeClass
{
    template<typename ...Arguments>
    void invokeQueued(CSignal<Arguments...>& signal, const Arguments&... args)
    {
        m_queue.emplace_back([=](){signal.invoke(args...);});
    }

    std::deque<std::function<void (void)>> m_queue;
};

問題:

CSignal<float> signal;
int i = 0;
SomeClass().invokeQueued(signal, i);

錯誤:

template parameter 'Arguments' is ambiguous
could be 'float'
or       'int'

可能的天真解決方案

template<typename ...FormalArguments, typename ...ActualArguments>
void invokeQueued(CSignal<FormalArguments...>& signal, const ActualArguments&... args)
{
    m_queue.emplace_back([=](){signal.invoke(args...);});
}

是不是在這種特殊情況下可以接受的,因為我需要通過值來捕獲的參數(將它們復制到拉姆達),並從轉換ActualArgumentsFormalArguments必須在時間發生invokeQueued被調用,而不是當拉姆達被調用。

如果我可以typedef的參數在包裝CSignal類,我會做:

template<typename ...FormalArguments>
void invokeQueued(CSignal<FormalArguments...>& signal, const CSignal<FormalArguments...>::argument_types&... args)
{
    m_queue.emplace_back([=](){signal.invoke(args...);});
}

但這似乎不可能。 解決方案?

您遇到的錯誤是因為參數的類型不同,而編譯器對於每個這樣的一對只有一個類型模板參數:從CSignal的簽名它看到float ,而從第二個參數的推導類型它看到int ,並且兩者都必須與Arguments包的單個元素匹配。 這就是歧義的由來。

要解決此問題,您可以通過引入非推導 context 來從模板參數推導中排除參數之一,例如使用以下標識技巧:

template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;

class SomeClass
{
public:
    template <typename... Arguments>
    void invokeQueued(CSignal<Arguments...>& signal,
                      const identity_t<Arguments>&... args)
    //                      ~~~~~~~~~^
    {
        m_queue.emplace_back([=](){signal.invoke(args...);});
    }

    std::deque<std::function<void(void)>> m_queue;
};

編譯器不會嘗試推導出屬於嵌套名稱說明符語法一部分的任何模板參數,這基本上就是identity所做的 - 它引入了identity<T>::type語法,因此T留給作用域解析運算符,但是它仍然可以在函數聲明中完整地使用。

演示


或者,您可以存儲在 lambda 表達式 (C++14) 中捕獲參數時轉換為正確類型的參數的衰減副本:

#include <utility>
#include <type_traits>
#include <cstddef>

class SomeClass
{
public:
    template <typename... FormalArguments, typename... ActualArguments>
    void invokeQueued(CSignal<FormalArguments...>& signal, ActualArguments&&... args)
    {
        invokeQueued(signal, std::index_sequence_for<ActualArguments...>{}, std::forward<ActualArguments>(args)...);
    }

    template <typename... FormalArguments, typename... ActualArguments, std::size_t... Is>
    void invokeQueued(CSignal<FormalArguments...>& signal, std::index_sequence<Is...>, ActualArguments&&... args)
    {
        m_queue.emplace_back(
          [signal, t = std::tuple<std::decay_t<FormalArguments>...>(std::forward<ActualArguments>(args)...)]
          (){signal.invoke(std::get<Is>(t)...);});
    }

    std::deque<std::function<void(void)>> m_queue;
};

演示 2

使用標識技巧為Arguments&&...創建一個非推導的上下文

template <typename T>
class Identity
{
public:
using type = T;
};

template<typename ...Arguments>
void invokeQueued(CSignal<Arguments...>& signal, const typename Identity<Arguments>::type &... args)
{
    m_queue.emplace_back([=](){signal.invoke(args...);});
}

暫無
暫無

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

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