3
votes

Summary

For some reason, my member function template that invokes a lambda function fails to compile with error C2275 ... illegal use of this type as an expression, but when the function is moved out to be a free function it compiles correctly.

Details

First I have a base class that keeps function instances in a vector. Only derived classes can add function instances to that vector by calling add_external. All function instances can be publicly invoked by calling invoke_externals. A derived class will add lambdas as function instances. Those lambdas will in turn call the base class function template invoke_internal with another "inner" lambda. The template parameter to invoke_internal is an exception type that will be explicitly caught when executing the "inner" lambda in invoke_internal:

using namespace std;

class base
{
public:
    void invoke_externals()
    {
        for (auto it = funcs_.begin(); it != funcs_.end(); ++it)
        {
            (*it)();
        }
    }

protected:
    void add_external(function<void(void)> func)
    {
        funcs_.push_back(func);
    }

    template <typename T>
    void invoke_internal(function<void(void)> func)
    {
        try
        {
            func();
        }
        catch (const T&){}
        catch (...){}
    }

    vector<function<void(void)>> funcs_;
};

Then I have two trivial free functions that throws logic_error and runtime_error exceptions. These functions are to be used in the "inner" lambda that is invoked in invoke_internal:

void throws_logic_error()
{
    throw logic_error("");
}

void throws_runtime_error()
{
    throw runtime_error("");
}

In the derived class constructor, two lambdas are added with add_external. Each of those lambdas call invoke_internal with "inner" lmbdas. The first call to invoke_internal will explicitly catch the logic_error that throws_logic_error will throw. The second call to invoke_internal will explicitly catch the runtime_error that throws_runtime_error will throw.

class derived : public base
{
public:
    derived()
    {
        add_external([this]()
        {
            invoke_internal<logic_error>([]()
            {
                throws_logic_error();
            });
        });

        add_external([this]()
        {
            invoke_internal<runtime_error>([]()
            {
                throws_runtime_error();
            }); 
        });
    }
};

And to tie all this together, derived is instantiated and invoke_externals is called to invoke the "external" lambdas added in the derived constructor. Those "external" lambdas will in turn invoke the "inner" lambdas and the thrown exceptions will be explicitly caught:

int wmain(int, wchar_t*[])
{
    derived().invoke_externals();

    return 0;
}

Problem

However, the above don't compile:

error C2275: 'std::logic_error' : illegal use of this type as an expression
error C2275: 'std::runtime_error' : illegal use of this type as an expression

...is issued for the calls to invoke_internal in the derived constructor.

If I move invoke_internal out from base and make it a free function, then it compiles.

Question

Why do I get error C2275 ... illegal use of this type as an expression when the function template is a base member?

Note: Moving the offending function out of base isn't optimal since in my real life scenario the function actually do use its class' state in different ways.

2

2 Answers

5
votes

Thanks to @sehe's answer, I could test this myself on VS2010. The following code works:

derived()
{   //                       vvvvvvvvvvvvvv
    add_external([this] () { this->template invoke_internal<logic_error>([]() { throws_logic_error(); }); });

    add_external([this] () { this->template invoke_internal<runtime_error>([]() { throws_runtime_error(); }); });
}   //                       ^^^^^^^^^^^^^^

Don't ask me why.Generally, the error that you get means, that the template, where the type was used, was not detected as such.

Normally this should only occur with dependent types / nested templates and can be solved with a template directly before the template in question (as shown), which tells the compiler that a template follows (duh). We need this-> before that though, because else it would look like an explicit instantiation, which would be wrong on its own:

template Foo<int>; // explicitly instantiate the Foo class template for int

Now. strangly, this problem also occurs here and I can only agree with @sehe that this looks like a compiler limitation.

3
votes

That would look like a compiler limitation. It compiles fine on gcc 4.6 with --std=c++0x

In case anyone else wants to try, I did some work to actually copy/past this into a proper, compiling, TU:

#include <vector>
#include <functional>
#include <stdexcept>
using namespace std;

class base
{
    public:

        void invoke_externals()
        {
            for (auto it = funcs_.begin(); it != funcs_.end(); ++it)
            {
                (*it)();
            }
        }

    protected:

        void add_external(function<void(void)> func)
        {
            funcs_.push_back(func);
        }

        template <typename T>
            void invoke_internal(function<void(void)> func)
            {
                try
                {
                    func();
                }
                catch (const T&)
                {
                }
                catch (...)
                {
                }
            }

        vector<function<void(void)>> funcs_;
};

void throws_logic_error()   { throw logic_error("");   }
void throws_runtime_error() { throw runtime_error(""); }

class derived : public base
{
    public:

        derived()
        {
            add_external([this] () { invoke_internal<logic_error>([]() { throws_logic_error(); }); });

            auto g = [this] () { invoke_internal<runtime_error>([]() { throws_runtime_error(); }); };
            add_external(g);
        }
};

int main(int, char*[])
{
    derived().invoke_externals();

    return 0;
}