2

When attempting to access a tuple's value via a scoped and typed enum, I get an error saying there is no matching type for std::get.

enum class User: std::size_t {
    FirstName = 1,
    LastName = 2,
};

std::tuple<std::string, std::string> user("Bobby", "Bean");

// Error
std::cout << std::get<User::FirstName>(user) << std::endl;

Given that std::get expects type std::size_t and the underlying type of the enum is also std::size_t, why does this fail?

I'm aware that I can cast the value of the enum, but I'm not sure why that would be necessary given that both underlying types are the same. With an unscoped enum, this works just fine.

Ian
  • 24,116
  • 22
  • 58
  • 96
  • 2
    It's strongly typed - you have to cast it explicitly. See [this](http://stackoverflow.com/questions/8357240/how-to-automatically-convert-strongly-typed-enum-into-int). – LogicStuff Nov 15 '15 at 19:00
  • It's an interesting question, but please don't just say "Error". Put the complete error message into the question. While you're at it, you can make everyone's lifes easier by adding `int main` and the necessary `#include`s. The goal is to copy'n'paste your code without any modification to a local or online editor and test it with an actual compiler. – Christian Hackl Nov 15 '15 at 19:42
  • Xcode didn't give me a more complex error, it highlighted it before I tried to compile. – Ian Nov 15 '15 at 19:44

3 Answers3

6

Enum classes really aren't integers on the surface. Just like a struct containing just an int is not an int.

The C++ standard way back to the integer representation is this:

using UserUndT = std::underlying_type<User>::type;
std::cout << 
   std::get<static_cast<UserUndT>(User::FirstName)>(user) << std::endl;

Also, check out this question: Using enable_if and underlying_type in function signature in VS2012

An alternative worth considering: (C++14)

#include <iostream>
#include <tuple>

struct FirstName {
   std::string val;
};

struct LastName {
   std::string val;
};

using FullName = std::tuple<FirstName,LastName>;

int main() {
  auto user = FullName({"John"},{"Deer"});
  std::cout << std::get<FirstName>(user).val << std::endl;
  std::cout << std::get<LastName>(user).val << std::endl;
}
Community
  • 1
  • 1
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • I was going to say, "but the member type is", then it occurred to me that this isn't the case.... – Ian Nov 15 '15 at 19:42
  • Is your c++14 considered to be a better design pattern? It seems like an interesting approach, though it results in a lot more classes being created as the number of fields increases. – Ian Nov 15 '15 at 20:19
  • 1
    I have use similar things with great results for readability and type safety (not with enum or tuples). I think I learned it first from *Scott Meyers's Effective C++, item 18 Make interfaces easy to use correctly and hard to use incorrectly* where he has small classes for Day, Month, Year. He makes it more type safe with `struct Day {explicit Day(int d):val(d){} int val;};`. Also: https://www.reddit.com/r/cpp/comments/3qbg70/cppcon_2015_kyle_markley_extreme_type_safety_with/ – Johan Lundberg Nov 15 '15 at 21:43
  • It's actually in that book (in the section on scoped enums) that uses enums in the way I did above. Edit: sorry, it was in the other book, 'effective modern c++' – Ian Nov 15 '15 at 21:49
0

to get what you want you can do:

namespace User{enum User:size_t{FirstName,LastName};};

then you will have the bare typed enum again.

Wolfgang Brehm
  • 1,491
  • 18
  • 21
0

The namespace solution works but not inside a function or class. An alternative is as follows:

#include <iostream>
#include <tuple>
#include <vector>
#include <string>
using namespace std;

int main()
{
   struct Test{enum size_t{first, second, third};};
   vector<tuple<int, string, bool>> mt;
   mt.emplace_back(make_tuple(10, "hello", true));
   cout << get<Test::first>(mt[0]) << ' ' << get<Test::second>(mt[0]) << ' ' << get<Test::third>(mt[0]) << endl;
   return 0;
}
dakka
  • 29
  • 3