我使用修改 C 中的函式的高階函式或模板做了很多作業
通常有用的模式是函式修飾符。例如,我經常使用這個簡單的掛鐘基準測驗:
#include <chrono>
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock, typename... Args>
auto wallclock(const auto& f, Args&... a){
auto start = Clock::now();
auto ret = f(a...);
return std::make_tuple(ret,(std::chrono::duration<std::ratio<1,Unit>(Clock::now() - start).count());
}
這甚至可以保持參考引數的完整性,并很好地描述函式。
但是,此呼叫不適用于 void 函式,我最終不得不撰寫一個具有不同名稱的函式來呼叫它們,如下所示:
#include <chrono>
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock, typename... Args>
auto wallclock_but_void(const auto& f, Args&... a){
auto start = Clock::now();
return f(a...), (std::chrono::duration<std::ratio<1,Unit>(Clock::now() - start).count();
}
我想這沒關系,但在更復雜的情況下它會變得煩人。我想知道是否有一種好方法可以根據任意可呼叫的回傳型別專門化函式或模板,以便介面可以相同,我一直在想也許我可以用概念來做到這一點。就像也許
template <typename F, typename... Args>
concept vreturn = requires(F&& f, Args&... a){
{ std::invoke(std::forward<F>(f), std::forward<Args>(a)...) } -> std::same_as<void>;
};
template <typename F, typename... Args>
concept nvreturn = !vreturn<F, Args...>;
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock, typename... Args>
auto wallclock(const auto& f, Args&... a) requires vreturn<decltype(f), Args...> {
auto start = Clock::now();
return f(a...), (Clock::now() - start).count();
}
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock, typename... Args>
auto wallclock(const auto& f, Args&... a) requires (!vreturn<decltype(f), Args...>) {
auto start = Clock::now();
auto ret = f(a...);
return std::make_tuple(ret, (Clock::now() - start).count());
}
但是,即使我使用 void 函式呼叫它,這仍然與非 void 版本匹配。我不確定如何獲得我想要的行為,但我覺得它必須是可能的
編輯:(最小可重復的例子)
因為有人問,失敗的代碼是(有了上面的定義)
#include <iostream>
#include <random>
void plusassign(int a, int b, int& x){
x = a b;
}
int plus(int a, int b){
return a b;
}
int main(int, char**){
std::random_device rd;
std::default_random_engine dre;
dre.seed(rd());
std::uniform_int_distribution<int> dist(0,0xFFFFFFFF);
int a = dist(dre)
, b = dist(dre)
, x;
auto t = wallclock(plusassign, a, b, x);
std::cout << a << " " << b << " = " << x << " (" << t << "ns)\n";
auto [c, t2] = wallclock(plus, a, b);
std::cout << a << " " << b << " = " << c << " (" << t2 << "ns)\n";
return 1;
}
這給了我一個 g 11.2.0 的編譯器錯誤(那個“auto ret”無效地分配了一個 void 結果)
uj5u.com熱心網友回復:
您的問題只是您沒有正確轉發引數。替換Args&為Args&&到處,也替換a...為std::forward<Args>(a)...。然后它將按預期作業。
只是Args&你永遠不會推斷出值類別,Args并且std::invoke總是會嘗試使用右值呼叫可呼叫物件,如果函式引數是非 const 左值參考,則會失敗。
std::invoke此外,鑒于您在概念中使用它,我建議保持一致并在函式定義中使用。std::invoke允許比簡單的呼叫運算式更多形式的函式呼叫。
uj5u.com熱心網友回復:
不用概念,你可以簡單地if constexpr在函式體中使用來判斷 callable 的回傳型別是否為void,并回傳合適的型別。
#include <functional>
#include <tuple>
#include <chrono>
template<size_t Unit = 1000, class Clock = std::chrono::steady_clock,
typename F, typename... Args>
auto wallclock(const F& f, Args&... a) {
auto start = Clock::now();
if constexpr (std::same_as<std::invoke_result_t<const F&, Args&...>, void>)
return f(a...), (Clock::now() - start).count();
else
return std::make_tuple(f(a...), (Clock::now() - start).count());
}
演示
uj5u.com熱心網友回復:
我找到了一種方法來做到這一點,這對我來說非常奇怪:
template <typename F, typename... Args>
concept vreturn = requires(F&& f, Args&... a){
{ std::invoke(std::forward<F>(f), std::forward<Args>(a)...) } -> std::same_as<void>;
};
template <typename F, typename... Args>
concept nvreturn = !vreturn<F, Args...>;
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock>
auto wallclock(const auto& f) requires vreturn<decltype(f)> {
auto start = Clock::now();
return f(), (Clock::now() - start).count();
}
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock, typename... Args>
auto wallclock(const auto& f, Args&... a) requires vreturn<decltype(f), Args...> {
return wallclock([&](){ f(a...); });
}
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock>
auto wallclock(const auto& f) requires (!vreturn<decltype(f)>) {
auto start = Clock::now();
auto ret = f();
return std::make_tuple(ret, (Clock::now() - start).count());
}
template <size_t Unit = 1000, class Clock = std::chrono::steady_clock, typename... Args>
auto wallclock(const auto& f, Args&... a) requires (!vreturn<decltype(f), Args...>) {
return wallclock([&](){ return f(a...); });
}
這將正確地模式匹配 void 與非 void 函式......但為什么?
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/437753.html
