0

I use TBB at multiple places in my project. But it seems that since I update Visual Studio from 15.6.X (X beeing the latest version) to 15.7.1 I get a compiler error at several places, telling me

[...]tbb\task_group.h(94): error C2530: 'my_task': references must be initialized

Looking at the referenced code (tbb/task_group.h):

//! Base class for types that should not be assigned.
class no_assign {
    // Deny assignment
    void operator=( const no_assign& );
public:

#if __GNUC__
    //! Explicitly define default construction, because otherwise gcc issues gratuitous warning.
    no_assign() {}
#endif /* __GNUC__ */
};

//! Base class for types that should not be copied or assigned.
class no_copy: no_assign {
    //! Deny copy construction
    no_copy( const no_copy& );
public:
    //! Allow default construction
    no_copy() {}
};

// ...

class ref_count_guard : internal::no_copy {
    task& my_task;  // compiler error occurs here
public:
    ref_count_guard( task& t ) : my_task(t) {
        my_task.increment_ref_count();
    }
    ~ref_count_guard() {
        my_task.decrement_ref_count();
    }
};

I don't see why the compiler is complaining there, as the reference is initialized by the constructor. Finding the problem in my code is also not that easy, because the compiler error occurs in every single source file that uses TBB and I don't think I changed anything since my last successful compilation (besides updating VS).

One possibility that comes to my mind is related to this question. If msvc somehow inherits the base class constructors by default, a default constructor would be inherited explaining the error. But testing this scenario seems to disprove it (as the code compiles).

Why is msvc complaining here?

Update

This minimal example reproduces the error on my system:

#include <vector>
#include <tbb/tbb.h>
#include <tbb/flow_graph.h>       

void main()
{
    std::vector<int> src{1, 2, 3, 4, 5};
    tbb::parallel_for_each(src.begin(), src.end(), [](int) { });
}

Update 2

Looks like just including tbb/tbb.h causes the error to occur. I don't even need to call anything. Rebuilding tbb with the new compiler version didn't help either.

Edit

Cross issue on github.

Timo
  • 9,269
  • 2
  • 28
  • 58
  • "`// Deny assignment void operator=( const no_assign& );`" is better/more explicitly accomplished by `void operator(const no_assign&) = delete;` and you don't even need the `no_assign` class at all. Just `= delete` the assignment operator in the class that should not be assignable instead of inheriting from `no_assign`. Less code, more explicit; win, win. – Jesper Juhl May 10 '18 at 14:56
  • @JesperJuhl That is TBB internal code. I didn't write that. I know `= delete` is a better option... – Timo May 10 '18 at 14:58
  • "` Explicitly define default construction` ..." - just use `= default` then the default constructor will also be trivial rather than user defined (which is desirable when possible). – Jesper Juhl May 10 '18 at 14:59
  • @JesperJuhl once again: the code that is shown above is **not** my code. It's from the TBB header files. – Timo May 10 '18 at 15:00
  • Are you sure that's a valid invocation of `parallel_for()`? The [docs](https://www.threadingbuildingblocks.org/docs/help/reference/algorithms/parallel_for_func.html) don't make much sense to me, but suggests that it (a) takes the range by reference to const and (b) the body has to take the range itself as a reference to non-const? – Barry May 10 '18 at 15:25
  • @Barry sorry I messed that up a bit. I've updated the question. – Timo May 10 '18 at 15:28
  • @JesperJuhl TBB compatible with C++03 and they can't use `= delete` – ntfs.hard May 12 '18 at 12:24
  • It looks like it's the same question on the official github https://github.com/01org/tbb/issues/53 – ntfs.hard May 12 '18 at 19:34
  • 1
    @ntfs.hard yes that issue was opened by me. I added the link here and on github. – Timo May 13 '18 at 13:47

2 Answers2

2

Simply removing /permissive- (e.g. set Comformance Mode to No in the C/C++ options) gets past this issue. I presume Intel will fix this soon.

Ben
  • 29
  • 5
1

This is a compiler bug when /permissive- option is used. It can be reproduced with the following code:

struct U {
    template<typename T>
    void foo() {
        class A {
            int& iref;
        public:
            A(int& ir) : iref(ir) { }
        };
        int the_answer = 42;
        A a(the_answer);
    }
};

int main() {
    U u;
    u.foo<int>();
    return 0;
}

The code is perfectly valid, compliant C++. As you can see, the reference is explicitly initialized in the member initializer list of the constructor. Also this is seemingly a regression in VS, because at least the initial release of VS 2017 (15.0.something) compiles this code with /permissive-.

Alexey Kukanov
  • 12,479
  • 2
  • 36
  • 55