I have the following code to detect if a type T has a function with a specific return type and parameters parameters (simplified version generated by some more complex macros):
#include <vector>
#include <complex>
#include <type_traits>
namespace has_private
{
template <typename...>
using void_t = void;
template <typename T>
struct identity
{
using type = T;
};
template <typename T>
struct add_const
{
using type = const T;
};
} // namespace has_private
template <typename T, typename = void>
struct has_bytes : std::false_type
{
static_assert(sizeof(T) > 0, "Incomplete type");
};
template <typename T>
struct has_bytes<
T,
has_private::void_t<decltype(std::declval<typename has_private::add_const<T&>::type>().bytes())>>
: std::is_same<decltype(std::declval<typename has_private::add_const<T&>::type>().bytes()), size_t>
{
};
template <typename T, typename = void>
struct has_capacity : std::false_type
{
static_assert(sizeof(T) > 0, "Incomplete type");
};
template <typename T>
struct has_capacity<
T,
has_private::void_t<decltype(std::declval<typename has_private::add_const<T&>::type>().capacity())>>
: std::is_same<decltype(std::declval<typename has_private::add_const<T&>::type>().capacity()), size_t>
{
};
static_assert(has_capacity<std::vector<int>>::value, "should have");
static_assert(has_capacity<std::complex<int>>::value == false, "shouldn't have");
This code compiles fine with VS2019, GCC 7+, clang 7+ but with VS2015 it doesn't compile. If I remove the has_bytes or if I move has_bytes after has_capacity the code compiles with VS2015. By the looks of it this seems to be a compiler bug.
Is this a compiler bug? If yes, do you know any workarounds this bug?
I can't just move the has_bytes after has_capacity because these trait classes are generated using macros from multiple places and their behavior must be reliable.
EDIT:
This is the complete error message:
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1>ConsoleApplication1.cpp
1>source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(51): error C2338: should have
1>Done building project "ConsoleApplication1.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
EDIT: I'm using latest version of VS2019 with Visual Studio 2015 (v140) Toolchain. I'm not exactly sure how to check the version in this case
Related
In Visual Studio I get this compiler error with the below code which I think should compile fine (gcc 11.2 and clang 14.0 compile it). The non-type parameter pack (auto...) means I should be able to pass in a template with any number of non-type template parameters, but the compilation only succeeds when passing in a template that uses a single non-type template parameter.
Is there something I'm missing that will make this work in Visual Studio?
I'm using "/std:c++latest" on Visual Studio 2019, version 16.11.12
error C3201: the template parameter list for class template 'testValues::twoParams' does not match the template parameter list for template parameter 'X'
namespace testValues {
template<template<auto...> class X>
struct templatetemplateparam {};
template<int i>
struct oneParam {};
using oneParam_t = templatetemplateparam<oneParam>;
template<int i, int j>
struct twoParams {};
using twoParams_t = templatetemplateparam<twoParams>;
}
The equivalent code with a specific non-type parameter or a type parameter pack does compile, if that's relevant:
namespace testIntValues {
template<template<int...> class X>
struct templatetemplateparam {};
template<int i>
struct oneParam {};
using oneParam_t = templatetemplateparam<oneParam>;
template<int i, int j>
struct twoParams {};
using twoParams_t = templatetemplateparam<twoParams>;
}
namespace testTypes {
template<template<typename...> class X>
struct templatetemplateparam {};
template<typename>
struct oneParam {};
using oneParam_t = templatetemplateparam<oneParam>;
template<typename, typename>
struct twoParams {};
using twoParams_t = templatetemplateparam<twoParams>;
}
I've reported this to MS at it looks like a bug in Visual Studio to me: https://developercommunity.visualstudio.com/t/Compilation-error-C3021-when-using-value/10015492
I get what appears to be a Visual Studio bug when I compile time test if a type with a default template parameter has some property. In the minimal example below I use std::is_integer.
When compiling the following program with Visual Studio 2017
#include <type_traits>
#include <utility>
#include <algorithm>
template<typename U,
typename Enabled = typename std::enable_if<std::is_integral<U>::value>::type>
struct wrapper
{
};
template<typename U, typename storage_type>
using is_small = std::is_void<typename std::enable_if <
(sizeof(wrapper<U, char>) <= sizeof(storage_type))
>::type>;
I get the following output
1>bug.cpp(13): error C2027: use of undefined type 'wrapper<U,char>'
1>bug.cpp(13): note: see declaration of 'wrapper<U,char>'
The same program does compile on g++ 6.1.
The program does compile on Visual Studio when the default parameter Enable is removed. In addition, when I perform the same sizeof(...)<=sizeof(...) test in the following template member function the program compiles fine (with is_small removed).
struct c
{
template<typename U, typename storage_type>
typename std::enable_if<(sizeof(wrapper<U, char>) <= sizeof(storage_type))>::value
foo(U u, storage_type t)
{}
};
Somehow the issue is related to the definition of is_small.
Does anyone know what the issue is? Is it a Visual Studio bug? Is there a workaround?
Edit
Reduced version:
template<class, class> struct A { };
template<int> struct B { };
template<class T> constexpr auto x = sizeof(A<T, int>); // works
template<class T> struct C : B<sizeof(A<T, int>)> { }; // works
template<class T> using D = B<sizeof(A<T, int>)>; // doesn't work
A possible workaround could be to use x instead of is_small.
Bug report submitted: https://developercommunity.visualstudio.com/content/problem/204504/issues-with-sizeof-alias-templates-and-virtual-fun.html
The test program compiles with VS2015 but not with VS2017 RC. Below is the error I am getting while compiling the test program with VS2017 RC:
/* main.cpp */
#include <type_traits>
template <typename T>
struct A_impl
{
using type = std::false_type;
};
template <typename T>
using A = typename A_impl<T>::type;
template <bool... B>
struct logic_helper;
template <bool... B>
using none_t = std::is_same<logic_helper<B...>, logic_helper<(B && false)...>>;
template <typename... C>
struct Foo
{
// Compile error:
// error C3520: 'C': parameter pack must be expanded in this context
using FooType = none_t<A<C>::value...>;
};
int main()
{
Foo<int, int, int> foo;
return 0;
}
I would like to know why the newest Visual Studio fails to compile the code while the older version has no problem with it. If possible, is there a solution to this compile error for VS2017 RC?
I found a workaround to the problem, but I still can't understand why the compiler complains with my first approach:
template <typename... C>
struct Foo
{
// Workaround
using FooType2 = logic_helper<A<C>::value...>;
using FooType3 = logic_helper<(A<C>::value && false)...>;
using FooType4 = std::is_same<FooType2, FooType3>;
};
I'm trying to define custom type traits (to which i'm relatively new), that check if given type supports a specific operation. If i just have one type trait it works as intended, but if i put two type traits sequentially both of them behave as one (when i use second one, it returns same result as the first one).
I'm using Visual Studio 2015 (v140).
Code sample:
#include <type_traits>
template<typename T, typename = void>
struct is_comparable_by_equal_to
: std::false_type {};
template<typename T>
struct is_comparable_by_equal_to<T, typename std::enable_if<true,
decltype(std::declval<T>() == std::declval<T>(), (void)0)>::type>
: std::true_type {};
template<typename T, typename = void>
struct is_comparable_by_not_equal_to
: std::false_type {};
template<typename T>
struct is_comparable_by_not_equal_to<T, typename std::enable_if<true,
decltype(std::declval<T>() != std::declval<T>(), (void)0)>::type>
: std::true_type {};
class mClass {
public:
bool operator== (const mClass& obj) {
return false;
}
};
int main() {
printf("%s: %d\n", "is_comparable_by_equal_to",
is_comparable_by_equal_to<mClass>::value);
printf("%s: %d\n", "is_comparable_by_not_equal_to",
is_comparable_by_not_equal_to<mClass>::value);
}
Resulting output:
is_comparable_by_equal_to: 1
is_comparable_by_not_equal_to: 1
Intended result output:
is_comparable_by_equal_to: 1
is_comparable_by_not_equal_to: 0
Question is why and how to solve the problem? Also i'm open for improvements how to define such custom type traits.
I compiled the code with g++ -std=c++11 and it worked as intended.
As it turned out, problem was in Visual Studio 2015 compiler version (VS2015 update 2), when I updated to latest version (update 3) it worked as intended.
This code
#include <iostream>
#include <type_traits>
template<typename Head, typename... Tail>
struct Is_Admitted {
constexpr static bool value = Is_Admitted<Head>::value && Is_Admitted<Tail...>::value;
};
template<>
template<typename T>
struct Is_Admitted<T> : public std::false_type{};
template<> struct Is_Admitted<int> : public std::true_type{};
template<> struct Is_Admitted<double> : public std::true_type{};
int main()
{
std::cout << Is_Admitted<int, double>::value << '\n';
std::cout << Is_Admitted<int, char>::value << '\n';
}
compiles and run fine under GCC (>=4.7, with c++11, c++14 or c++17 support enabled)
compiles with a warning and run fine with clang 3.6 (with c++11, c++14 or c++17 support enabled)
does not compile under VS2015RC (or Preview) with the following errors:
(error descriptions are translated in English by myself as I was not able to set English as compiler language, so they might mismatch with original ones)
error C2910: 'Is_Admitted<T,>': impossible to perform explicit specialization
error C2065: 'value': undeclared identifier
error C2976: 'Is_Admitted': insufficients template arguments
error C2131: constant expression does not return any value
Which compiler is right and which one is wrong? Is that code compliant to either c++11, c++14 or c++17 standard?
And what is the right way to do what I'm trying to do, that is a variadic type function that returns true only if all template type parameters are of some admitted types?
You have an extra template<> here:
template<> // <===
template<typename T>
struct Is_Admitted<T> : public std::false_type{};
Your code gives me the same error via webcompiler.
Simply remove it and it compiles fine. I do not understand how this compiles on either gcc or clang.
Two template declarations are only necessary when you're defining a member template of a class template outside of the class definition, e.g.:
template <typename T>
struct Foo {
template <typename U>
void bar();
};
template <typename T>
template <typename U>
void Foo<T>::bar() { ... }