我正在努力解決我無法解決的問題。我在整個網站和網路上進行了搜索,但沒有找到任何解決方案(可能是因為我對某些東西不太了解,無法正確提問)。
問題如下,假設我有一個帶有一些引數的模板類,其中一個是模板本身,在class.h檔案中定義:
template <template <typename bar_type> typename Indicator, size_t count>
class MultiProgressBar
{
public:
template <typename... Indicators, typename = typename std::enable_if_t <(sizeof...( Indicators ) == count )>>
explicit MultiProgressBar( Indicators &... bars ) : bars_( { bars... } ) {}
private:
std::array <std::reference_wrapper<Indicator>, count> bars_;
};
在我的main.cpp檔案中,我希望能夠執行以下操作:
ProgressBar<int> prog_int;
ProgressBar<double> prog_double;
ProgressBar<float> prog_float;
MultiProgressBar <ProgressBar, 3> bars( prog_int, prog_double, prog_float );
在另一個標題中定義的模板類在哪里ProgressBar,例如progress.h
問題是當我編譯時,我得到了這個錯誤(我沒有發布整個makefile,因為它真的很復雜而且解釋起來很長,但是讓我們考慮到我正在編譯更多的東西,但唯一重要的是這個錯誤在這篇文章中給出):
g -g -Isrc -MMD -MP -c src/main.cpp -o obj/src/main.cpp.o
In file included from src/../include/osmanip.h:7,
from src/main.cpp:6:
src/../include/multi_progress_bar.h:48:50: error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> class std::reference_wrapper’
48 | std::array <std::reference_wrapper<Indicator>, count> bars_;
| ^
src/../include/multi_progress_bar.h:48:50: note: expected a type, got ‘Indicator’
src/../include/multi_progress_bar.h:48:58: error: template argument 1 is invalid
48 | std::array <std::reference_wrapper<Indicator>, count> bars_;
| ^
src/../include/multi_progress_bar.h: In instantiation of ‘osm::MultiProgressBar<Indicator, count>::MultiProgressBar(Indicators& ...) [with Indicators = {osm::ProgressBar<int>, osm::ProgressBar<double>, osm::ProgressBar<float>}; <template-parameter-2-2> = void; Indicator = osm::ProgressBar; long unsigned int count = 3]’:
src/main.cpp:245:77: required from here
src/../include/multi_progress_bar.h:23:77: warning: list-initializer for non-class type must not be parenthesized
23 | explicit MultiProgressBar( Indicators &... bars ) : bars_( { bars... } ) {}
| ^
src/../include/multi_progress_bar.h:23:77: error: cannot convert ‘<brace-enclosed initializer list>’ to ‘int’ in initialization
make: *** [makefile:65: obj/src/main.cpp.o] Error 1
你知道我的代碼有什么問題嗎?
uj5u.com熱心網友回復:
您還需要一個共同的基礎來存盤您存盤參考的元素。這可能是一種方式:
struct ProgressBarBase {
// Optional: A virtual destructor if you want to delete objects via
// base class pointers:
virtual ~ProgressBarBase() = default;
};
template <class bar_type>
struct ProgressBar : public ProgressBarBase {};
這樣,您可以稍微更改您的類以存盤對ProgressBarBase(或您想要的任何指標基類)的參考。
template <class T, std::size_t count>
class MultiProgressBar {
public:
template<class... Indicators, std::enable_if_t<sizeof...(Indicators) == count, int> = 0>
MultiProgressBar(Indicators&&... bars)
: bars_{std::forward<Indicators>(bars)...}
{
// or this instead of SFINAE:
static_assert(sizeof...(Indicators) == count, "wrong number of arguments");
}
private:
std::array<std::reference_wrapper<T>, count> bars_;
};
int main() {
ProgressBar<int> prog_int;
ProgressBar<double> prog_double;
ProgressBar<float> prog_float;
MultiProgressBar<ProgressBarBase, 3> bars(prog_int, prog_double, prog_float);
}
但是,例如,如果您有一個函式,例如void update(bar_type value);in ProgressBar,則將其virtual放在基類中將不起作用(因為bar_type在基類中不知道)。
一種選擇可能是放棄std::array并使用 a std::tuple。這使得保留型別資訊成為可能,并且還消除了對基類的需要。您也不需要,reference_wrapper因為參考不會存盤在陣列中。
C 17 示例:
template <class bar_type>
struct ProgressBar{
void update(bar_type v) {
std::cout << value << '\n';
value = v;
}
bar_type value;
};
template <class... Indicators>
class MultiProgressBar {
public:
template<class... Inds>
MultiProgressBar(Inds&&... bars) : bars_{std::forward<Inds>(bars)...} {}
void update() {
std::apply([](auto&... rw){
(rw.update(0), ...);
}, bars_);
}
private:
std::tuple<Indicators&...> bars_;
};
// deduction guide
template<class... Indicators>
MultiProgressBar(Indicators...) -> MultiProgressBar<Indicators...>;
C 17 演示
在完成上述操作之前,我沒有注意到 C 11 標記。這也是一個 C 11 示例。實作起來還有很多,但我現在想不出更簡單的方法:
#include <iostream>
#include <tuple>
#include <utility>
// A type to generate indices for parameter packs:
template<size_t... Is>
struct indices { };
template<size_t N, size_t... Is>
struct gen_indices : gen_indices<N - 1, N - 1, Is...> { };
template<size_t... Is>
struct gen_indices<0, Is...> : indices<Is...> { };
template <class bar_type>
struct ProgressBar{
void update(bar_type v) {
std::cout << value << '\n';
value = v;
}
bar_type value;
};
template <class... Indicators>
class MultiProgressBar {
public:
template<class... Inds>
MultiProgressBar(Inds&&... bars) : bars_{std::forward<Inds>(bars)...} {}
void update() {
// call the update overload what takes an indices<size_t...>
update(gen_indices<sizeof...(Indicators)>());
}
private:
template<size_t... Ids>
void update(indices<Ids...>) {
// call update on every element in the tuple
// , 0 is done to discard the `void` return from `update`
// to build a dummy initializer list (since C 11 lacks fold expressions)
auto dummy = { (std::get<Ids>(bars_).update(0), 0)... };
(void) dummy; // quiet warning about unused variable
}
std::tuple<Indicators&...> bars_;
};
// Since deduction guides doesn't exist in C 11, we'll add a helper function:
template<class... Indicators>
MultiProgressBar<typename std::remove_reference<Indicators>::type...>
make_MultiProgressBar(Indicators&&... inds) {
return {std::forward<Indicators>(inds)...};
}
int main() {
ProgressBar<int> prog_int{1};
ProgressBar<double> prog_double{2};
ProgressBar<float> prog_float{3};
auto bars = make_MultiProgressBar(prog_int, prog_double, prog_float);
bars.update();
// all set to 0:
std::cout << prog_int.value << prog_double.value << prog_float.value << '\n';
}
C 11 演示
For C 11 you could make it simpler by making it possible to provide functors containing the function you want to call for all elements in the tuple:
template <class... Indicators>
class MultiProgressBar {
public:
template<class... Inds>
MultiProgressBar(Inds&&... bars) : bars_{std::forward<Inds>(bars)...} {}
static size_t size() { return sizeof...(Indicators); }
template <class Func, class... Args>
void for_one(size_t idx, Func&& func, Args&&... args) {
call_one(idx, gen_indices<sizeof...(Indicators)>(),
std::forward<Func>(func), std::forward<Args>(args)...);
}
template<class Func, class... Args>
void for_each(Func func, Args&&... args) {
// call `call_all` that takes an indices<size_t...>
// with a function and the arguments to pass to the function
call_all(gen_indices<sizeof...(Indicators)>(), func, std::forward<Args>(args)...);
}
private:
template <size_t... Ids, class Func, class... Args>
void call_one(size_t idx, indices<Ids...>, Func&& func, Args&&... args) {
[](...) {} ( // define a dummy lambda that takes any arguments and call it
// with the below as arguments. Short-circuit evaluation makes sure
// that `func` only gets called when `idx == Ids`
(idx == Ids && // (void) below to avoid warnings about unused return vals
((void)std::forward<Func>(func)(
std::get<Ids>(bars_), std::forward<Args>(args)...),
false))...
);
}
template<size_t... Ids, class Func, class... Args>
void call_all(indices<Ids...>, Func&& func, Args&&... args) {
// call `func` with every element in the tuple
// ", 0" is done to discard the `void` return from `update`
// to build a dummy initializer list (since C 11 lacks fold expressions)
auto dummy = { (func(std::get<Ids>(bars_), args...), 0)... };
(void) dummy; // quiet warning about unused variable
}
std::tuple<Indicators&...> bars_;
};
A functor could then look like this:
template< class T > // borrowed from c 20
struct type_identity { using type = T; };
struct updater { // instead of a lambda with auto as argument type
template<template<class> class PB, class bar_type>
auto operator()(PB<bar_type>& pb,
typename type_identity<bar_type>::type v) const -> decltype(pb.update(bar_type{}))
{
return pb.update(v);
}
};
and be used like this:
int main() {
ProgressBar<int> prog_int{1};
ProgressBar<double> prog_double{2};
ProgressBar<float> prog_float{3};
auto bars = make_MultiProgressBar(prog_int, prog_double, prog_float);
bars.for_each(updater{}, 4);
// all set to 4:
std::cout << prog_int.value << '\n'
<< prog_double.value << '\n'
<< prog_float.value << '\n';
for(size_t i = 0; i < bars.size(); i) {
bars.for_one(i, updater{}, i);
}
// 0, 1 and 2
std::cout << prog_int.value << '\n'
<< prog_double.value << '\n'
<< prog_float.value << '\n';
}
Demo
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/425976.html
上一篇:無法使用執行緒在終端上顯示多個進度條(來自我的班級),為什么?
下一篇:使用Enum表示天數
