I need to alias std::get
function in order to improve readability in my code.
Unfortunately I got a compile-time error get<0> in namespace ‘std’ does not name a type
. using
is equivalent to typedef
so it needs types to work with.
I am using a std::tuple
to represent some data type:
using myFoo = std::tuple<int,int,double,string>;
using getNumber = std::get<0>;
I look at some previous questions but the solution proposed is to wrap and use std::forward
. I don't want to write such code for each member.
Is there a way to get around this using only using keyword?
Function aliases of this type provide natural-language descriptions and prompting for input parameters to an underlying function rule. You need to create and test the function before defining a function alias rule that references it. You cannot define an alias for a function that returns a complex Java type.
You can use an alias declaration to declare a name to use as a synonym for a previously declared type. (This mechanism is also referred to informally as a type alias). You can also use this mechanism to create an alias template, which can be useful for custom allocators.
Type aliases Type aliases provide alternative names for existing types. If the type name is too long you can introduce a different shorter name and use the new one instead. It's useful to shorten long generic types. For instance, it's often tempting to shrink collection types: typealias NodeSet = Set<Network.
Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.
is there a way to get around this using only using keyword?
I would say no, for std::get
is not a type (thus it's not eligible for such an use).
Moreover, even if it was possible, note that std::get
is an overloaded function, thus you would have been required to bind yourself to a specific implementation.
That said, in C++17, you can do something like this:
#include<tuple>
#include<utility>
using myFoo = std::tuple<int,int,double>;
constexpr auto getNumber = [](auto &&t) constexpr -> decltype(auto) { return std::get<0>(std::forward<decltype(t)>(t)); };
template<int> struct S {};
int main() {
constexpr myFoo t{0,0,0.};
S<getNumber(t)> s{};
(void)s;
}
As you can see, constexpr
lambdas and variables help you creating compile-time (let me say) wrappers you can use to rename functions.
As correctly pointed out by @T.C. in the comments, if you want to generalize it even more and get an almost perfect alias for std::get
, you can use a variable template:
template<int N>
constexpr auto getFromPosition = [](auto &&t) constexpr -> decltype(auto) { return std::get<N>(std::forward<decltype(t)>(t)); };
Now you can invoke it as it follows:
S<getFromPosition<0>(t)> s{};
See it on wandbox.
You can do it with a using
+ an enum
:
#include<tuple>
using myFoo = std::tuple<int,int,double>;
int main() {
constexpr myFoo t{0,0,0.};
enum { Number = 0 };
using std::get;
auto&& x = get<Number>(t);
(void)x;
}
Although unfortunately, this is not DRY since you have to maintain an enum and a tuple simultaneously.
In my view, the most DRY and safest way to achieve this is to use tagged values in the tuple. Limit the tuple to maximum one of each tag type.
The tag is essentially a mnemonic for some unique concept:
#include <tuple>
#include <iostream>
//
// simple example of a tagged value class
//
template<class Type, class Tag>
struct tagged
{
constexpr tagged(Type t)
: value_(t) {}
operator Type&() { return value_; }
operator Type const&() const { return value_; }
Type value_;
};
struct age_tag {};
struct weight_tag {};
struct height_tag {};
using Age = tagged<int, age_tag>;
using Weight = tagged<int, weight_tag>;
using Height = tagged<double, height_tag>;
int main()
{
constexpr auto foo1 = std::make_tuple(Age(21), Weight(150), Height(165.5));
constexpr auto foo2 = std::make_tuple(Weight(150), Height(165.5), Age(21));
using std::get;
//
// note below how order now makes no difference
//
std::cout << get<Age>(foo1) << std::endl;
std::cout << get<Weight>(foo1) << std::endl;
std::cout << get<Height>(foo1) << std::endl;
std::cout << "\n";
std::cout << get<Age>(foo2) << std::endl;
std::cout << get<Weight>(foo2) << std::endl;
std::cout << get<Height>(foo2) << std::endl;
}
expected output:
21
150
165.5
21
150
165.5
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With