NOTE: This bug only occurs with x64 projects in both release and debug modes.
Odd warnings involving std::chrono appear on this piece of code using VC2019 at warning level 3. This is a stripped down piece of code that processes command line flags. I've removed most of the guts that aren't relevant to the problem.
#if 1 // enable bug
#include <chrono> // excluding this also eliminates chrono warnings
using CorrectedIntType=int;
#else
using CorrectedIntType=size_t;
#endif
#include <iostream>
#include <vector>
#include <string>
#include <type_traits>
using std::vector;
using std::string;
namespace {
void fixup(const std::string& argcmd, std::string& arg) { arg = argcmd; }
template<class T>
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg)
{
fixup(arglist[idx], arg);
arglist.erase(arglist.begin() + idx);
}
template<class T, class ...TA>
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg, TA&...argv)
{
procVal(arglist, idx, arg);
procVal(arglist, idx, argv...);
}
template<class T, class ...TA>
bool procFlag(const char* pc, std::vector<std::string>& arglist, T& arg1, TA&...argv)
{
std::string flag(pc);
for (size_t i = 0; i < arglist.size(); i++)
{
if (arglist[i] == flag)
{
arglist.erase(arglist.begin() + i);
procVal(arglist, i, arg1); // process one argument after flag
return true;
}
}
return false;
}
}
int main()
{
string outfile;
vector<string> test = { "test" };
procFlag("-o", test, outfile); // assigns test[0] to outfile and removes it
std::cout << outfile << '\n';
}
Warnings:
1>Source.cpp
1>C:\Users\mgray\Documents\Visual Studio 2017\Projects\CommandLineCPP\stackoverflow\Source.cpp(35,1): warning C4267: 'argument': conversion from 'size_t' to 'CorrectedIntType', possible loss of data
1>C:\Users\mgray\Documents\Visual Studio 2017\Projects\CommandLineCPP\stackoverflow\Source.cpp(54): message : see reference to function template instantiation 'bool `anonymous-namespace'::procFlag<std::string,>(const char *,std::vector<std::string,std::allocator<std::string>> &,T &)' being compiled
1> with
1> [
1> T=std::string
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(632): message : see reference to class template instantiation 'std::chrono::duration<double,std::ratio<1,1>>' being compiled
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(178): message : see reference to class template instantiation 'std::chrono::duration<__int64,std::nano>' being compiled
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(610): message : see reference to class template instantiation 'std::chrono::time_point<std::chrono::steady_clock,std::chrono::nanoseconds>' being compiled
While the code works, even with the int -<> size_t conversion issue which is a legitimate warning, all warnings go away when the macro at the top is set to 0. So somehow the size difference between size_t and int triggers chrono messages. It concerns me that the chrono warnings exist since it isn't involved. Is this a bug in VS2019? Any ideas as to why the chrono warning references are occurring?
This is a valid warning and it is not related to <chrono> but to your own code and CorrectedIntType type. Here is a simplified code without <chrono>: https://gcc.godbolt.org/z/qf9v8TEh7
In the definition of procVal, the second parameter is CorrectedIntType:
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg)
but it is called from procFlag with size_t value:
bool procFlag(const char* pc, std::vector<std::string>& arglist, T& arg1)
...
for (size_t i = 0; i < arglist.size(); i++)
...
procVal(arglist, i, arg1);
So one can fix the warning by changing i type to CorrectedIntType as well.
Related
I try to compile a personal project on Visual Studio 2019 (using MSVC 19.28 compiler) and I came accross a compilation error in the std::visit which I don't understand:
<source>(131): error C2653: '`global namespace'': is not a class or namespace name
C:/data/msvc/14.28.29914/include\type_traits(1493): note: see reference to function template instantiation 'auto CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>::operator ()<const _First&>(_T1) const' being compiled
with
[
_First=bool CmdLineOpts::* ,
_T1=bool CmdLineOpts::* const &
]
C:/data/msvc/14.28.29914/include\variant(1654): note: see reference to alias template instantiation 'std::_Variant_visit_result_t<CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>,const std::variant<bool CmdLineOpts::* >&>' being compiled
<source>(120): note: while compiling class template member function 'void CommandLineOptionsParser<CmdLineOpts>::register_callback(const CommandLineOption &,std::variant<bool CmdLineOpts::* >)'
<source>(83): note: see reference to function template instantiation 'void CommandLineOptionsParser<CmdLineOpts>::register_callback(const CommandLineOption &,std::variant<bool CmdLineOpts::* >)' being compiled
<source>(142): note: see reference to class template instantiation 'CommandLineOptionsParser<CmdLineOpts>' being compiled
<source>(123): error C2672: 'visit': no matching overloaded function found
<source>(131): error C2893: Failed to specialize function template 'unknown-type std::visit(_Callable &&,_Variants &&...)'
C:/data/msvc/14.28.29914/include\variant(1654): note: see declaration of 'std::visit'
<source>(131): note: With the following template arguments:
<source>(131): note: '_Callable=CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>'
<source>(131): note: '_Variants={const std::variant<bool CmdLineOpts::* > &}'
<source>(131): note: '<unnamed-symbol>=void'
Compiler returned: 2
This code compiles fine with gcc.
I tested the code snippet from cppreference on std::visit and it compiles with MSVC, so I am not so sure what the issue here.
I simplified the code and reproduced the issue on godbolt
Here's the code
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <set>
#include <string_view>
#include <variant>
#include <type_traits>
using InvalidArgumentException = std::invalid_argument;
using CommandLineOption = std::string;
template <class Opts>
class CommandLineOptionsParser : Opts {
public:
using OptionType = std::variant<bool Opts::*>;
using CommandLineOptionWithValue = std::pair<CommandLineOption, OptionType>;
Opts parse(const char* argStr) {
// First register the callbacks
bool Opts::* pBool = &Opts::help;
register_callback("help", pBool);
for (auto& cbk : _callbacks) {
cbk.second(0, argStr);
}
return static_cast<Opts>(*this);
}
private:
using callback_t = std::function<void(int, const char *)>;
std::map<CommandLineOption, callback_t> _callbacks;
void register_callback(const CommandLineOption& commandLineOption, OptionType prop) {
_callbacks[commandLineOption] = [this, &commandLineOption, prop](int idx, const char * argv) {
if (std::string(argv) == commandLineOption) {
std::visit([](auto&& a) {
using T = std::decay_t<decltype(a)>;
if constexpr (std::is_same_v<T, bool Opts::*>) {
std::cout << "bool" << std::endl;
}
},
prop);
}
};
};
};
struct CmdLineOpts {
bool help{};
};
int main(int argc, const char* argv[])
{
CommandLineOptionsParser<CmdLineOpts> p;
CmdLineOpts cmdLineOptions = p.parse("opt1");
}
It seems MSVC is having difficulty synthesizing a lambda with a pointer-to-member argument in a template context.
I tried to simplify it to a MCVE, hopefully it captures the essence of the issue:
template<class T>
bool test(int T::* t) {
return [](int T::* x) {
return true;
}(t);
}
struct A {
int a;
};
int main() {
return test<A>(&A::a);
}
It fails to compile in MSVC C++20 mode (but not C++17) with a similar nonsensical error (link):
<source>(5): error C2653: '`global namespace'': is not a class or namespace name
<source>(13): note: see reference to function template instantiation 'bool test<A>(int A::* )' being compiled
<source>(5): error C2664: 'bool test::<lambda_1>::operator ()(A *) const': cannot convert argument 1 from 'int A::* ' to 'A *'
<source>(5): note: There is no context in which this conversion is possible
<source>(5): note: see declaration of 'test::<lambda_1>::operator ()'
I would suggest to report this to the vendor.
As a potential workaround can try extracting the lambda into a functor class with a templated operator(), it seems to compile (example).
I have this code in my source:
template <std::size_t... Dims>
class DimensionPack {
public:
using Dimensions = std::index_sequence<Dims...>;
static const std::size_t total_dimensions = sizeof...(Dims);
std::vector<unsigned int> even_or_odd;
public:
DimensionPack() {
unsigned idx = 0;
for ( ; idx < total_dimensions; idx++ ) {
//MatrixDimensionOddOrEven mdoe( Dimensions[idx] );
//unsigned int val = mdoe.even_or_odd;
//even_or_odd.push_back( val );
}
}
};
The commented lines of code is the code in question. I'm not familiar with using std::indx_sequence<> and I've read the documentation from MSD on it and still not sure about it especially when it is used with the using directive.
This template class will be used as a parameter pack for other variadic template classes for storing and extracting the data from the variadic parameter list. Within this class's constructor I'm using this structure's constructor and private method to check values and to return its state of either being even or odd and getting the value from its public constant member:
struct MatrixDimensionOddOrEven {
const unsigned int even_or_odd;
explicit MatrixDimensionOddOrEven( unsigned int odd_or_even ) : even_or_odd( test( odd_or_even ) ) {}
private:
const unsigned int test( unsigned int value ) const {
if ( value == 0 ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << "invalid number: " << value << " must be >= 1.";
Logger::log( strStream, Logger::TYPE_ERROR );
throw ExceptionHandler( strStream );
}
return ( ((value % 2) == 0) ? ODD : EVEN );
}
};
The cpp file to this header file compiles, but when I go to compile another cpp file such as main that includes it; it fails to compile.
This is the current error messages that I'm getting:
1>------ Build started: Project: FileTester, Configuration: Debug Win32 ------
1> main.cpp
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(44): error C2540: non-constant expression as array bound
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(41): note: while compiling class template member function 'DimensionPack<2,3,4>::DimensionPack(void)'
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\main.cpp(336): note: see reference to function template instantiation 'DimensionPack<2,3,4>::DimensionPack(void)' being compiled
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(60): note: see reference to class template instantiation 'DimensionPack<2,3,4>' being compiled
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\main.cpp(155): note: see reference to class template instantiation 'Matrix<float,2,3,4>' being compiled
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): error C2228: left of '.even_or_odd' must have class/struct/union
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): error C2789: 'val': an object of const-qualified type must be initialized
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): note: see declaration of 'val'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
It isn't so much the compiler errors that are giving me trouble; it is more about this syntax that is sort of new to me within the use of variadic templates and parameter packs. So how do I properly write the syntax to get the individual elements of this std::index_sequence<...> that is assigned to Dimensions with the using directive in order to pass those values to the constructor of the helper structure that is seen in the for loop within the DimensionPack class?
There is no need for index_sequence. This will do:
template <std::size_t... Dims>
class DimensionPack {
public:
std::vector<unsigned int> even_or_odd;
public:
DimensionPack()
: even_or_odd{MatrixDimensionOddOrEven{Dims}.even_or_odd...}
{
}
};
As a bonus, the beauty of it is that you don't need to push_back each element. You can directly initialize the vector with all the elements.
Why would you do this?
template <std::size_t... Dims>
class DimensionPack {
public:
using odd_dims = std::integer_sequence<bool,
std::enable_if_t<Dims!=0, bool>(Dims%2)...
>;
constexpr static std::array<bool, sizeof...(Dims)> get_odd_dims() {
return {{ (bool)(Dims%2)... }};
}
};
now odd_dims is a integer_sequence of which dimensions are even and odd.
get_odd_dims returns a constexpr array of bools of which dimensions are odd. This is easier to iterate over than a integer sequence.
The odd_dims type will fail to compile if any Dims are 0. No need for runtime checks.
Dynamic allocation here on a per-DimensionPack basis seems a very strange way to solve this.
I have the below class (which is basically a wrapper for std::vector), and one of the functions returns a variable of the type AInteger (which is basically a wrapper for int). Now the type AInteger is used multiple times throughout the class, yet the compiler starts complaining at a very specific position. When I remove the "getSize()" function, everything compiles just fine.
There is mutual inclusion, so I need the forward declaration for everything to work.
One of the problems is that the AList class is a template, so it is impossible to move the definitions to a .cpp file (which would normally solve the problem).
What am I doing wrong?
Here's the class:
#ifndef ALIST_H
#define ALIST_H
#include <vector>
#include "AInteger.h"
class AInteger;
template<typename VALUE>
class AList {
public:
AList() {
}
AList(const std::vector<VALUE> list) {
value = list;
}
~AList() {
}
operator const std::vector<VALUE>() const {
return value;
}
std::vector<VALUE> toStdVector() const {
return value;
}
VALUE operator [](const AInteger index) const {
return value.at(index);
}
void add(const VALUE value) {
this->value.push_back(value);
}
VALUE get(const AInteger index) const {
return value[index];
}
AInteger getSize() const { // ERROR OCCURS HERE
return value.size();
}
void remove(const AInteger index) {
value.erase(index);
}
private:
std::vector<VALUE> value;
};
#endif
Output:
1>------ Build started: Project: ALibrary, Configuration: Debug Win32 ------
1> ASocket.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1> AInteger.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1> AHttpRequest.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1> ABoolean.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1> Generating Code...
1> Compiling...
1> AString.cpp
1> Generating Code...
2>------ Build started: Project: BitHoarder, Configuration: Debug Win32 ------
2> SystemHandler.cpp
2>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
2> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
2> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
2> Main.cpp
2>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
2> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
2> d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
2> Generating Code...
========== Build: 0 succeeded, 2 failed, 0 up-to-date, 0 skipped ==========
If any more code is needed just ask and I will provide.
Thanks!
EDIT:
Here's the code that's inside "AInteger.h"
#ifndef AINTEGER_H
#define AINTEGER_H
#include "AList.h"
template<typename VALUE>
class AList;
class AInteger {
public:
AInteger();
AInteger(const int);
~AInteger();
operator const int() const;
int toInt() const;
AInteger operator=(const AInteger &);
AInteger & operator++();
AInteger & operator--();
AList<AInteger>splitByNumber(const AInteger &);
private:
int value;
};
#endif
To make matters even more confusing, the following class, which does THE EXACT SAME THING does not produce the error:
#ifndef ADICTIONARY_H
#define ADICTIONARY_H
#include <map>
#include "AInteger.h"
class AInteger;
template<typename KEY, typename VALUE, typename COMPARE = std::less<KEY>>
class ADictionary {
public:
ADictionary() {
}
ADictionary(const std::map<KEY, VALUE, COMPARE> dictionary) {
value = dictionary;
}
~ADictionary() {
}
operator const std::map<KEY, VALUE, COMPARE>() const {
return value;
}
std::map<KEY, VALUE, COMPARE> toStdMap() const {
return value;
}
VALUE operator [](const KEY key) const {
return value.at(key);
}
void add(const KEY key, const VALUE value) {
this->value.insert(std::make_pair(key, value));
}
VALUE get(const KEY key) const {
return value[key];
}
AInteger getSize() const { // No error here
return value.size();
}
void remove(const KEY key) {
value.erase(value.find(key));
}
private:
std::map<KEY, VALUE, COMPARE> value;
};
#endif
The implementation of getSize() must create an instance of AInteger. This is only possible if the full definition of AInteger is known. You cannot create an instance of a type which is only forward-declared.
And that's exactly what the compiler tells you: error C2027: use of undefined type 'AInteger'. It does not ignore the forward declaration but tells you that it's not enough.
As for the #include "AInteger.h", you do not show its contents, but possible problems are:
Buggy include guard in the header file causes the compiler to skip the definition.
The header file defines a different type with a similar name.
The definition of AInteger is within a namespace.
What's gone wrong is answered very well in Christian Hackl's answer and normally I'd drop it there, but I can't explain how the OP can fix this properly in a comment.
First a revised AInteger.h
#ifndef AINTEGER_H
#define AINTEGER_H
template<typename VALUE>
class AList;
class AInteger {
public:
AInteger();
AInteger(const int);
~AInteger();
//operator const int() const;
int toInt() const;
AInteger operator=(const AInteger &);
AInteger & operator++();
AInteger & operator--();
std::unique_ptr<AList<AInteger>> splitByNumber(const AInteger &);
void splitByNumber(const AInteger &,
AList<AInteger> &);
private:
int value;
};
#endif
The include of AList.h is gone. The forward declaration of AList remains I've provided two different splitByNumber methods. Pick one. The first creates and returns a pointer protected by a smart pointer. The second approach, and my personal preference, takes a reference to an AList created by the caller.
The thing is neither know anything about the inner workings of AList because all they care is about is A) It exists and b) they can get the address of one.
Bear with me while I explore both because I think they are both educational.
Alist.h remains unchanged except for the removal of the forward declaration of AInteger.
The two splitByNumber candidates sit in AInteger's implementation file which can safely include both AInteger.h and AList.h and thus has complete knowledge of both at the same time and can do all the magical things AIntegers and ALists can do.
std::unique_ptr<AList<AInteger>> AInteger::splitByNumber(const AInteger & integer)
{
std::unique_ptr<AList<AInteger>> listp(new AList<AInteger>());
// do stuff with listp
return listp;
}
void AInteger::splitByNumber(const AInteger & integer,
AList<AInteger> & list)
{
// do stuff with list
}
Using the pointer version:
std::unique_ptr<AList<AInteger>> alistp = aitest1.splitByNumber(aitest2);
alistp->add(somenumber);
Using the reference version:
AList<AInteger> alist; // first create an empty AList
aitest1.splitByNumber(aitest2, alist); // pass it into the function
alist.add(somenumber); // use the AList
template<typename T>
void f(const T &v = T());
template<>
void f<std::string>(const std::string &v)
{
std::cout << v;
}
int main(int argc, char* argv[])
{
f<std::string>(); // Error in VS2013, OK in VS2012, gcc-4.7
f<std::string>("Test"); // OK
f<std::string>(std::string()); //OK
return 0;
}
The latest Visual Studio 2013 compiler gives the following compiler error for the case when the default argument must be used:
error C2440: 'default argument' : cannot convert from 'const std::string *' to 'const std::string &'
Reason: cannot convert from 'const std::string *' to 'const std::string'
No constructor could take the source type, or constructor overload resolution was ambiguous
Visual Studio 2012 and gcc-4.7 compile fine.
Update: As it seems to be a VS2013 bug, are there any temporary workarounds that do not require significant code changes until this is fixed by MS? Bug report was submitted on MS connect.
Whenever I see this kind of problems with template functions, I try to switch to template structures (if you need a temporary workaround)...
template<typename T>
struct foo
{
static void f(const T &v = T());
};
template<>
struct foo<std::string>
{
static void f(const std::string &v = std::string())
{
std::cout << v;
}
};
Unfortunately, I can't check this in Visual Studio 2013 because I don't have it, but I hope it should work.
The downside here is that you should explicitly specify your type, it's no longer deducted
foo<std::string>::f()
foo<std::string>::f("Text")
My wild guess here would be adding like a wrapper function:
template<typename T>
void f_wrapper(const T &v = T())
{
foo<T>::f(v);
}
With the code below, I get a very confusing error in MSVC that seems to suggest the key type (an std::tuple) is being converted to an std::string.
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <unordered_map>
typedef std::tuple<std::string,int,char> key_t;
struct key_hash : public std::unary_function<key_t, std::size_t>
{
std::size_t operator()(const key_t& k) const
{
return std::get<0>(k)[0] ^ std::get<1>(k) ^ std::get<2>(k);
}
};
struct key_equal : public std::binary_function<key_t, key_t, bool>
{
bool operator()(const key_t& v0, const key_t& v1) const
{
return (
std::get<0>(v0) == std::get<0>(v1) &&
std::get<1>(v0) == std::get<1>(v1) &&
std::get<2>(v0) == std::get<2>(v1)
);
}
};
struct data
{
std::string x;
};
typedef std::unordered_map<key_t,data,key_hash,key_equal> map_t;
int main()
{
map_t m;
data d;
d.x = "test data";
m[std::make_tuple("abc",1,'X')] = d;
auto itr = m.find(std::make_tuple(std::string("abc"),1,'X'));
if (m.end() != itr)
{
std::cout << "x: " << itr->second.x;
}
return 0;
}
Error:
Error 1 error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'const std::tr1::tuple<_Arg0,_Arg1,_Arg2>' to 'const std::basic_string<_Elem,_Traits,_Ax> &' c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\tuple 127 1
Compiler: MS Visual Studio 2010
On ideone, I get the following even more convoluted error:
http://ideone.com/yEv2j
I can't seem to figure out where I've gone wrong.
The problem for ideone is that key_t already exists:
prog.cpp:7:42: error: conflicting declaration 'typedef class std::tuple<std::basic_string<char>, int, char> key_t'
/usr/include/sys/types.h:123:17: error: 'key_t' has a previous declaration as 'typedef __key_t key_t'
Rename your key_t to something else, or put it into some namespaces.
Your code works after this change in both g++ and clang++. I believe this is a bug in MSVC.
Strange. Your code works fine in Visual Studio 2012 RC and output is "x: test data".