簡體   English   中英

非模板類的可變參數成員

[英]Variadic members in non-template class

我正在嘗試編寫具有模板構造函數的類Invocation

template<typename F>
class Invocation {
public:
    template<typename... Args>
    Invocation(F&& f, Args&&... args)
    { /* store f and args somewhere for later use */ }

    ...
};

通常我會同時使用FArgs...參數化Invocation類本身,但是在這種情況下,我需要給定F的統一類型,所以我試圖找到一種方法來存儲args...內部的任何類型一個Invocation<F> ,以盡可能減少性能損失。 (這可能不是最佳設計,但可能是一個有趣的練習。)

一種想法是使用虛函數:

template<typename F>
class ArgsBase {
public:
    // discard return value
    virtual void invoke(F&& f) = 0;
};

template<typename F, typename... Ts>
class Args : public ArgsBase<F> {
public:
    Args(Ts&&... args) : args_(std::forward<Ts>(args)...) {}

    void invoke(F&& f) override
    {
        /* somehow call f with args_ (something like std::apply) */
        ...
    }

private:
    std::tuple<Ts&&...> args_;
};

然后在Invocation<F>類中,例如,可以有一個std::unique_ptr<ArgsBase<F>>成員,該成員指向在Invocation<F> ctor中創建的Args<F, Ts...>對象。 。 我們可以在需要時調用其invoke虛擬方法。

這只是我提出的一個隨機想法。 還有其他方法可以做到這一點嗎? 理想情況下沒有虛擬功能或類似功能的開銷?

更新:感謝建議使用std::function或lambdas的評論/答案。 我應該明確指出,我實際上對更一般的情況感興趣,即,可變參數可能不是可調用對象的參數。 它可以是我想存儲在其類型未由這些東西的類型參數化的類中的任何東西。

如果您嘗試保存帶有其參數的函數調用以供以后調用,則可以使用打包在std::function對象中的lambdas:

template<typename F, typename ... Args>
std::function<void()> createInvocation(F f, const Args& ... args)
{
    return [f,args...]() { f(args...); };
}

然后,您可以像這樣使用它:

void myFunc(int a, int b)
{
    std::cout << "Invoked: " << a + b << std::endl;
}

int main() {

    auto invocation = createInvocation(myFunc, 1, 2);

    invocation();

    return 0;

}

更新:如果要創建通用的非模板容器類型,則可以將元組包裝到本身從非模板類型派生的類型中。 然后,主要問題是訪問基礎數據。 這可以通過創建一個靜態函數分派表來解決,該表針對給定的元組類型重定向查詢,從而可以使用動態提供的函數參數來調用需要編譯時常量索引模板參數的std::get 這是實現此目的的實現:

class GenericTupleContainer
{
public:

    virtual const void* getItemAtIndex(size_t index) = 0;
};

template<typename ... T>
class TupleContainer : public GenericTupleContainer
{
public:

    TupleContainer(T&& ... args)
        : data(std::forward<T>(args)...)
        {}

    const void* getItemAtIndex(size_t index) override
    {
        if(index >= sizeof...(T))
            throw std::runtime_error("Invalid index");

        return dispatchTable[index](data);
    }

private:

    template<size_t index>
    static const void* getItemAtIdx(const std::tuple<T...>& data)
    {
        return &std::get<index>(data);
    }

    using GetterFn = const void*(*)(const std::tuple<T...>&);

    static GetterFn* initDispatchTable()
    {
        static GetterFn dispatchTable[sizeof...(T)];
        populateDispatchTable<sizeof...(T)>(dispatchTable, std::integral_constant<bool, sizeof...(T) == 0>());
        return dispatchTable;
    }

    static GetterFn* dispatchTable;

    template<size_t idx>
    static void populateDispatchTable(GetterFn* table, std::false_type);

    template<size_t idx>
    static void populateDispatchTable(GetterFn* table, std::true_type) 
    {
        //terminating call - do nothing
    }

    std::tuple<T...> data;
};

template<typename ... T>
typename TupleContainer<T...>::GetterFn* TupleContainer<T...>::dispatchTable = TupleContainer<T...>::initDispatchTable();

template<typename ... T>
template<size_t idx>
void TupleContainer<T...>::populateDispatchTable(GetterFn* table, std::false_type)
{
    table[idx-1] = &TupleContainer<T...>::template getItemAtIdx<idx-1>;
    populateDispatchTable<idx-1>(table, std::integral_constant<bool, idx-1 == 0>() );
}

template<typename ... T>
auto createTupleContainer(T&& ... args)
{
    return new TupleContainer<T...>(std::forward<T>(args)...);
}

然后,您可以按以下方式使用以上內容:

int main() {

    GenericTupleContainer* data = createTupleContainer(1, 2.0, "Hello");

    std::cout << *(static_cast<const int*>(data->getItemAtIndex(0))) << std::endl;
    std::cout << *(static_cast<const double*>(data->getItemAtIndex(1))) << std::endl;
    std::cout << (static_cast<const char*>(data->getItemAtIndex(2))) << std::endl;

    return 0;

}

從上面的用法可以看出,已經實現了將任意模板化的元組包裝為非模板化類型的目的,從而可以使用常規(函數)索引參數而不是模板一。 現在,這種吸氣劑的返回類型必須是通用的,因此我選擇在此處使用void* ,這不是理想的選擇。 但是您可以開發出這種想法,以使該容器提供有關其數據元組成員類型的更多有用信息。 另外,請注意,這確實使用了虛函數。 通過進一步的工作,您也可以擺脫這種情況,盡管您將無法擺脫至少一個函數指針查找(即調度表中的查找)-這是獲得靈活性的代價。能夠使用運行時值索引到元組。

如評論中所述,我不必擔心按值存儲參數。 編譯器的復制刪除功能可能很慷慨。

特別是如果您為該類提供r值調用:

#include <tuple>

template<typename F>
class ArgsBase {
public:
    // discard return value
    virtual void invoke(F&& f) const & = 0;
    virtual void invoke(F&& f) && = 0;
};

template<typename F, class... FunctionArgs>
class Args : public ArgsBase<F> {
public:
  template<class...Ts>
    Args(Ts&&... args) : args_(std::forward<Ts>(args)...) {}

    template<std::size_t...Is, class Tuple>
      static void invoke_impl(F& f, std::index_sequence<Is...>, Tuple&& t)
    {
      f(std::get<Is>(std::forward<Tuple>(t))...);
    }


    void invoke(F&& f) const & override
    {
      invoke_impl(f, 
                  std::make_index_sequence<std::tuple_size<tuple_type>::value>(), 
                  args_); 
        /* somehow call f with args_ (something like std::apply) */
    }

    void invoke(F&& f) && override
    {
      invoke_impl(f, 
                  std::make_index_sequence<std::tuple_size<tuple_type>::value>(), 
                  std::move(args_)); 
        /* somehow call f with args_ (something like std::apply) */
    }

private:
  using tuple_type = std::tuple<FunctionArgs...>;
    tuple_type args_;
};

template<class Callable, class...MyArgs>
  auto later(MyArgs&&...args) {
  return Args<Callable, std::decay_t<MyArgs>...>(std::forward<MyArgs>(args)...);
}

void foo(const std::string&, std::string)
{
}

int main()
{
  auto l = later<decltype(&foo)>(std::string("hello"), std::string("world"));
  l.invoke(foo);
  std::move(l).invoke(foo);
}

暫無
暫無

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

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