0

This is about as simplified as I could make a toy example that still hit the bug:

struct Vector3f64 {
  double x;
  double y;
  double z;
};

struct Vector3f32 {
  float x;
  float y;
  float z;
};

// I use this to select their element type in functions:
template <typename T>
using param_vector = std::conditional_t<std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>, Vector3f64>, double, float>;

// This is the function I want to pull the return type from:
template <typename T>
T VectorVolume(const T x, const T y, const T z) {
  return x * x + y * y + z * z;
}

template<class R, class... ARGS>
std::function<R(ARGS...)> make_func(R(*ptr)(ARGS...)) {
  return std::function<R(ARGS...)>(ptr);
}

// This function fails to compile:
template <typename T>
typename decltype(make_func(&VectorVolume<param_vector<T>>))::result_type func(const T& dir) {
  return VectorVolume(dir.x, dir.y, dir.z);
}

int main() {
  const Vector3f64 foo{ 10.0, 10.0, 10.0 };

  std::cout << func(foo) << std::endl;
}

The make_func is from SergyA's answer which I wanted to create a std::function so I could find a return type without explicitly declaring the parameters VectorVolume took. But I get this error from version 15.6.7:

error C2039: result_type: is not a member of 'global namespace' error C2061: syntax error: identifier func error C2143: syntax error: missing ; before { error C2447: {: missing function header (old-style formal list?) error C3861: func: identifier not found

This works fine on in g++: https://ideone.com/PU3oBV It'll even work fine on if I don't pass the using statement as a template parameter:

template <typename T>
typename decltype(make_func(&VectorVolume<double>))::result_type func(const T& dir) {
    return VectorVolume(dir.x, dir.y, dir.z);
}

This is almost identical to the problem I worked around here: Templated usings Can't be Nested in Visual Studio Unfortunately, in that case I could just replace my function construction with a result_of call. In this case I just don't see how I can redesign make_func to work around this bug. Does anyone know of a workaround? (Other than upgrading to 15.9.5, which does solve this.)

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    I'd like to help but ... do you know an online compiler reproducing the error? – YSC Mar 07 '19 at 15:50
  • @YSC Try https://rextester.com/l/cpp_online_compiler_visual – Paul Sanders Mar 07 '19 at 15:52
  • @YSC Regrettably I do not :( If you have a suggestion I'd be happy to attempt it locally though. – Jonathan Mee Mar 07 '19 at 15:53
  • Thanks @PaulSanders; do you know how to enable C++17 though? – YSC Mar 07 '19 at 15:54
  • Nevermind, found. – YSC Mar 07 '19 at 15:55
  • @JonathanMee What compiler settings are you using (in particular, which `/std:c++XX` version)? – Max Langhof Mar 07 '19 at 15:55
  • @MaxLanghof necessarily 17 (`is_same_v`) – YSC Mar 07 '19 at 15:56
  • 1
    [This version](https://godbolt.org/z/3EqcW8) complains about the return type too, but not exactly the same. I commented the workaround, worth a try I guess. – felix Mar 07 '19 at 15:57
  • @MaxLanghof I'm just using the default. So no argument there. It seems to accept `is_same_v` without my using "/std:c++17" – Jonathan Mee Mar 07 '19 at 16:02
  • @felix Wow! That does work for me. I was hoping to use the type in an `enable_if_t` on the return type. But... beggars can't be choosers. If no one has a way to get this working within the return line, I'll accept. – Jonathan Mee Mar 07 '19 at 16:07
  • @felix I wonder if we could wrap `make_func` in a `struct` to avoid having to do a using inside the function? – Jonathan Mee Mar 07 '19 at 16:12
  • [This traits like workaround works too](https://godbolt.org/z/qZ-fR9). [And this too](https://godbolt.org/z/YH8xMg). It seems that msvc isn't comfortable if `decltype()` appears at lhs of the scope operator (::). Since `make_func`'s return type depends on `make_func`'s template parameter `T`, which need to be deduced, I don't think wrapping it into struct will help. – felix Mar 07 '19 at 16:38
  • I was wrong. Wrapping it into a struct might work by using deduction guide, if you really need... – felix Mar 07 '19 at 16:51
  • @felix I finally solved this: https://stackoverflow.com/a/55050902/2642059 I really appreciate your help. If you'll post your `using` that was internal to the the function, I'd like to at least give a +1 nod to that. It's not the best solution for me, but it may be for others working around this bug. – Jonathan Mee Mar 07 '19 at 18:56

2 Answers2

1

As workaround, you might simply do:

template <typename T>
auto func(const T& dir)
-> decltype(VectorVolume(dir.x, dir.y, dir.z))
{
    return VectorVolume(dir.x, dir.y, dir.z);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I'm giving this a +1 because it compiles... However it violates my requirements: "The `make_func` is from [SergyA's answer](https://stackoverflow.com/a/54990286/2642059) which I wanted to create a `std::function` so I could find a return type without explicitly declaring the parameters `VectorVolume` took." – Jonathan Mee Mar 07 '19 at 18:55
0

You're really only interested in the function::result_type so there's really no need to go through the bugged path of returning a function. Just return the result type and do a decltype on that (you don't even need to define the function since you're not actually calling it.) Something like this:

template <typename R, typename... ARGS>
R make_func(R(*)(ARGS...));

Then just directly use the return type:

template <typename T>
decltype(make_func(&VectorVolume<param_vector<T>>)) func(const T& dir) {
    return VectorVolume(dir.x, dir.y, dir.z);
}

This is works great on Visual Studio 15.6.7 and as an added bonus is fully compatible: https://ideone.com/gcYo8x

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288