deductible parameter type still requires explicit reference cast, e.g., std::thread

The following piece of code (which is supposed to crash) is compilable using MSVC but not in g++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <unordered_map>
#include <thread>

using namespace std;

void thread_add(unordered_map<int, int>& ht, int from, int to)
{
    for(int i = from; i <= to; ++i)
        ht.insert(unordered_map<int, int>::value_type(i, 0));
}

void main()
{
    unordered_map<int, int> ht;
    thread t[2];

    t[0] = thread(thread_add, ht, 0, 9);
    t[1] = thread(thread_add, ht, 10, 19);

    t[0].join();
    t[1].join();

    std::cout << "size: " << ht.size() << std::endl;
}


g++ gives the following error:
error: no type named ‘type’ in ‘class std::result_of ...

g++ require explicit reference cast to compile:
t[0] = thread(thread_add, ref(ht), 0, 9);

Although MSVC compiles successfully but it does not run correctly, because it passes in a temporarily created object instead of the reference to the original object, which is even worse.

The g++ compilation error is saying that the compiler cannot decide whether to pass in the reference of the original object or a newly created temporary object. However, this is unnecessary because it can be easily deduced from the definition of the thread_add function whether to pass in the reference of the original object or the temporarily created object, e.g.,
void thread_add(unordered_map<int, int>& ht, int from, int to);
void thread_add(unordered_map<int, int> ht, int from, int to);

I hope the compiler can be improved to resolve all these deducible parameter types so that programmers do not need to explicitly write the reference cast std::ref() every time, like what has been done in MSVC.
Last edited on
I'm pretty sure the g++ implementation is the actually correct one. Just because a compiler that makes sense of these things could be written doesn't mean it should, if the standard doesn't allow it.
In principle, it also could be possible to make a compiler capable of compiling this:
1
2
3
4
5
auto x;
auto y;
auto z;
x = f();
z = x + y;
But the standard doesn't allow this kind of type inference.

Whether the standard should allow (that is, require) compilers to be this smart is a different discussion.
> Although MSVC compiles successfully but it does not run correctly, because it passes in a
> temporarily created object instead of the reference to the original object, which is even worse.

This is the correct behaviour as per the standard.
The std::thread object holds a copy of the object, a reference to this copy is passed to the function.

The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref or std::cref).
http://en.cppreference.com/w/cpp/thread/thread/thread


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <unordered_map>
#include <thread>
#include <mutex>

using namespace std;

void thread_add( std::unordered_map<int, int>& ht, int from, int to)
{
    for(int i = from; i <= to; ++i)
    {
        static std::mutex map_lock ;
        std::lock_guard<std::mutex> lock_it(map_lock) ; 
        ht.insert( std::unordered_map<int, int>::value_type(i, 0) );
    }
}

// void main()
int main() 
{
    std::unordered_map<int, int> ht;
    std::thread t[2];

    t[0] = std::thread( thread_add, std::ref(ht), 0, 9); // to pass by reference, wrap the reference
    t[1] = std::thread( thread_add, std::ref(ht), 10, 19); // to pass by reference, wrap the reference

    t[0].join();
    t[1].join();

    std::cout << "size: " << ht.size() << std::endl;
}

http://coliru.stacked-crooked.com/a/0d56c0235c5d85ed
http://rextester.com/URWEBT98471
Topic archived. No new replies allowed.