设置元组的值,类型从常量的std::array, …> 中推断
Greggs 是一个类,其内部包含元组 sausage_roll 和常量数组 flavours。这个元组包含 ints和 strings,而数组由 variants构成。
The makeOrder method should set a value in the sausage_roll tuple, knowing its type based on the flavours variant at that particular index. This method must be callable at runtime.
#include <array>
#include <iostream>
#include <string>
#include <tuple>
#include <variant>
template<class... AllArgs>
class Greggs {
public:
constexpr Greggs(AllArgs... args) : flavours({args...}) {}
bool makeOrder(std::string order, size_t index) {
if (index >= this->flavours.size())
return false;
const std::variant<int, std::string> flavour = this->flavours[index];
if (flavour.index() == 0) { // Checks if the variant is int
int value = std::stoi(order);
this->setSausageRollArg(index, value);
}
else {
this->setSausageRollArg(index, order);
}
return true;
}
private:
template<typename T>
void printOrder(T value) const {
std::cout << "Sausage roll contains " << value << " onions." << std::endl;
}
template<std::size_t I = 0, typename T>
inline typename std::enable_if<I == sizeof...(AllArgs), void>::type
setSausageRollArg(int, T) { }
template<std::size_t I = 0, typename T>
inline typename std::enable_if<I < sizeof...(AllArgs), void>::type
setSausageRollArg(int index, T value) {
if (index == 0) printOrder(std::get<I>(this->sausage_roll) = value); // Sets the tuple arg
setSausageRollArg<I + 1, T>(index-1, value);
}
std::tuple<AllArgs...> sausage_roll;
const std::array<std::variant<int, std::string>, sizeof...(AllArgs)> flavours;
};
int main() {
// auto two_types = new Greggs(0, std::string(""));
// two_types->makeOrder("Vegan", 1);
// auto one_int_type = new Greggs(0);
// one_int_type->makeOrder("1", 0);
auto one_str_type = new Greggs(std::string(""));
one_str_type->makeOrder("73", 0);
return 0;
}
This program builds successfully, but when uncommenting the other four lines in main(), The following compile error is given:
main.cpp:36:59: error: assigning to 'typename tuple_element<1UL, tuple<string, int>>::type' (aka 'int') from incompatible type 'std::string'
36 | if (index == 0) std::get<I>(this->sausage_roll) = value; // Sets the tuple arg
Expected output:
Sausage roll contains Vegan onions
Sausage roll contains 1 onions
Sausage roll contains 73 onions
Online: https://godbolt.org/z/cnWEsvPhG
解决方案
When index != 0 the code to set the value is still compiled even though it isn't run. When the types don't match you're going to get an error. Using if constexpr you can check if the assignment is well formed before using it. If it's not, the code won't be compiled when the types aren't convertible.
https://godbolt.org/z/Yd9EWK5f6
setSausageRollArg(int index, T value) {
if (index == 0) {
if constexpr (auto& entry = std::get<I>(this->sausage_roll); requires{ entry = value; }) {
entry = value;
printOrder(value);
}
}
setSausageRollArg<I + 1, T>(index-1, value);
}