3

I've got a nasty problem with some code and can't see the error. I have a template function

template<typename T, typename F> void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
    CheckedIncrement();
    auto new_expr = Wide::make_unique<T>();
    new_expr->lhs = std::move(cur_expr);
    new_expr->rhs = f();
    cur_expr = std::move(new_expr);
}

with the obvious implementation of make_unique. There are other overloads, but they differ in obvious ways like different parameter numbers, so I know that this is the only one being called. Then I call it, like so (this is the error line, 315):

RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression>(current_expression, [&] { return this_ptr->RecursiveParseExpression(); });

But the compiler insists that I am using a type as an expression. I'm pretty sure that this code is completely legal. Am I missing something? The error is

1>parser.cpp(315): error C2275: 'Wide::Parser::AccessExpression' : illegal use of this type as an expression

Edit: These are the definitions of the other overloads.

template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST> RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseSingleToken;
    RecursiveParseSingleToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseSingleToken();
        }
    };
    RecursiveParseSingleToken();
    return std::move(cur_expr);
}

template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseDualToken;
    RecursiveParseDualToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseDualToken();
        case second:
            RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
            return RecursiveParseDualToken();
        }
    };
    RecursiveParseDualToken();

    return std::move(cur_expr);
}

However, they clearly differ in requiring additional explicit template arguments, and so cannot be the call resolved.

Edit: The definition of make_unique:

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }
};

Just to be certain, the calling code isn't in any template of any kind, and there are absolutely no dependent names. Wide is a namespace.

Another edit: The error is repeated for all the direct calls to this overload, but not for the ones which are called through the other overloads, curiously.

Edit: This 100line test case reproduces the problem.

#include <memory>
#include <functional>
#include <vector>

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }
    class Lexer {
    public:
        enum TokenType {
            Namespace,
            For,
            Identifier
        };
        struct Token {
            TokenType type;
        };
    };
    class Parser {
    public:
        struct ExpressionAST {};
        struct BinaryExpressionAST : public ExpressionAST {
            std::unique_ptr<ExpressionAST> lhs;
            std::unique_ptr<ExpressionAST> rhs;
        };
        struct AccessExpression : public BinaryExpressionAST {};
        struct IdentifierExpression : public ExpressionAST {};
    };
}

typedef std::vector<Wide::Lexer::Token>::iterator Iterator;

struct inner_parser {
    Iterator begin, end;

    std::unique_ptr<Wide::Parser::ExpressionAST> RecursiveParsePrimaryExpression() {
        if (begin->type == Wide::Lexer::TokenType::Identifier) {
            CheckedIncrement();
            return Wide::make_unique<Wide::Parser::IdentifierExpression>();
        }
        throw std::runtime_error("aah");
    }
    template<typename T, typename F> void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
        CheckedIncrement();
        auto new_expr = Wide::make_unique<T>();
        new_expr->lhs = std::move(cur_expr);
        new_expr->rhs = f();
        cur_expr = std::move(new_expr);
    }
    template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
    RecursiveParseLeftAssociativeBinaryExpression(F next) {
        auto cur_expr = next();
        std::function<void()> RecursiveParseSingleToken;
        RecursiveParseSingleToken = [&] {
            switch(begin->type) {
            case first:
                RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                return RecursiveParseSingleToken();
            }
        };
        RecursiveParseSingleToken();
        return std::move(cur_expr);
    }

    template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F>
    std::unique_ptr<Wide::Parser::ExpressionAST>
        RecursiveParseLeftAssociativeBinaryExpression(F next) {
            auto cur_expr = next();
            std::function<void()> RecursiveParseDualToken;
            RecursiveParseDualToken = [&] {
                switch(begin->type) {
                case first:
                    RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                    return RecursiveParseDualToken();
                case second:
                    RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
                    return RecursiveParseDualToken();
                }
            };
            RecursiveParseDualToken();

            return std::move(cur_expr);
    }

    void CheckedIncrement() {
        if (begin == end)
            throw std::runtime_error("aaaaaah!");
        begin++;
    }
};
int main() {
    std::vector<Wide::Lexer::Token> tokens;
    inner_parser ip;
    ip.begin = tokens.begin();
    ip.end = tokens.end();
    auto ret = ip.RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression, Wide::Lexer::TokenType::For>([&] { return ip.RecursiveParsePrimaryExpression(); });
    return 0;
}
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Which line is causing the problem? and the error message? – Nawaz Dec 09 '11 at 15:42
  • 2
    Is `Wide::Parser::AccessExpression` a nested type in a class template? Probably you need `typename`? – Nawaz Dec 09 '11 at 15:47
  • 1
    Um, which line is line 315 in the above code? – Raymond Chen Dec 09 '11 at 15:47
  • @Nawaz: No, the only template involved is the function template, and the class is defined before use. – Puppy Dec 09 '11 at 15:51
  • @RaymondChen: The call to the function template. – Puppy Dec 09 '11 at 15:51
  • @DeadMG: Now I suspect the problem is coming from the other overload of the function template. Please post their signatures. – Nawaz Dec 09 '11 at 15:53
  • @DeadMG: Also, 1) did you copy-paste the exact code? 2) Is the call from a member function of a class template which derive from another class? Provide more information – Nawaz Dec 09 '11 at 15:55
  • @Nawaz: Yes, I definitely did. You will see that the call could not possibly be any of the other signatures. – Puppy Dec 09 '11 at 15:55
  • Is it just me, or are you missing a template parameter in your function call? – MGZero Dec 09 '11 at 16:00
  • @MGZero: It's deduced from the argument? – Puppy Dec 09 '11 at 16:00
  • Can we see the definition of `make_unique()`? – Mike Seymour Dec 09 '11 at 16:04
  • @DeadMG Not sure what you mean. – MGZero Dec 09 '11 at 16:06
  • @MGZero: There are two template arguments, `T`, and `F`. `F` is deduced because I accept a parameter, `F f`. So the compiler can deduce `F` and there is no need for it to be explicitly passed. – Puppy Dec 09 '11 at 16:07
  • @MikeSeymour: Edited it in, but it's pretty trivial. – Puppy Dec 09 '11 at 16:08
  • @DeadMG Oh, I see. Didn't know you could do that with templates, interesting. – MGZero Dec 09 '11 at 16:12
  • Perhaps you can make a reduced (SSCCE) example; I wouldn't be stupefied if you ran into another MSVC++ buglet – sehe Dec 09 '11 at 16:13
  • @MGZero: Using them would be pretty damn arduous if you had to specify every argument every time. Template argument deduction is one of their basic features and has been since their first implementation. – Puppy Dec 09 '11 at 16:13

2 Answers2

2

Sprinkling this-> liberally inside of the template member functions' lambdas seems to fix your test case:

#include <memory>
#include <functional>
#include <vector>

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }
    class Lexer {
    public:
        enum TokenType {
            Namespace,
            For,
            Identifier
        };
        struct Token {
            TokenType type;
        };
    };
    class Parser {
    public:
        struct ExpressionAST {};
        struct BinaryExpressionAST : public ExpressionAST {
            std::unique_ptr<ExpressionAST> lhs;
            std::unique_ptr<ExpressionAST> rhs;
        };
        struct AccessExpression : public BinaryExpressionAST {};
        struct IdentifierExpression : public ExpressionAST {};
    };
}

typedef std::vector<Wide::Lexer::Token>::iterator Iterator;

struct inner_parser {
    Iterator begin, end;

    std::unique_ptr<Wide::Parser::ExpressionAST> RecursiveParsePrimaryExpression() {
        if (begin->type == Wide::Lexer::Identifier) {
            CheckedIncrement();
            return Wide::make_unique<Wide::Parser::IdentifierExpression>();
        }
        throw std::runtime_error("aah");
    }
    template<typename T, typename F> void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
        CheckedIncrement();
        auto new_expr = Wide::make_unique<T>();
        new_expr->lhs = std::move(cur_expr);
        new_expr->rhs = f();
        cur_expr = std::move(new_expr);
    }
    template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
    RecursiveParseLeftAssociativeBinaryExpression(F next) {
        auto cur_expr = next();
        std::function<void()> RecursiveParseSingleToken;
        RecursiveParseSingleToken = [&] {
            switch(this->begin->type) {
            case first:
                this->RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                return RecursiveParseSingleToken();
            }
        };
        RecursiveParseSingleToken();
        return std::move(cur_expr);
    }

    template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F>
    std::unique_ptr<Wide::Parser::ExpressionAST>
        RecursiveParseLeftAssociativeBinaryExpression(F next) {
            auto cur_expr = next();
            std::function<void()> RecursiveParseDualToken;
            RecursiveParseDualToken = [&] {
                switch(this->begin->type) {
                case first:
                    this->RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
                    return RecursiveParseDualToken();
                case second:
                    this->RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
                    return RecursiveParseDualToken();
                }
            };
            RecursiveParseDualToken();

            return std::move(cur_expr);
    }

    void CheckedIncrement() {
        if (begin == end)
            throw std::runtime_error("aaaaaah!");
        begin++;
    }
};
int main() {
    std::vector<Wide::Lexer::Token> tokens;
    inner_parser ip;
    ip.begin = tokens.begin();
    ip.end = tokens.end();
    auto ret = ip.RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression, Wide::Lexer::For>([&] { return ip.RecursiveParsePrimaryExpression(); });
    return 0;
}

I'd speculate that this is a bug in VC++ 2010's lambda implementation, wherein class-scope name resolution is not performed properly for member templates.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • Yes. I added explicit `this->` qualification, and it also solved the problem. Ironically, qualifying with `this->` should be illegal if it wasn't already at member scope... I agree that it's probably a lookup problem. In either case, the error message, at least, is still a compiler bug. – Puppy Dec 09 '11 at 19:08
1

After you've sprinkled typename everywhere you can think, sprinkle template after any qualified call that contains a < before a (. The template goes right after the ::.

Like this:

auto new_expr = Wide::template make_unique<T>();

Update

Don't know if this will help, but I reverse engineered the question to create a minimum complete compiling (but not linking) program. Unfortunately it does not replicate the symptom. But perhaps it will be helpful in tracking down the problem. Or at least serve as amusement as to how close I guessed in several places. ;-)

#include <memory>
#include <functional>

namespace Wide {
    template<typename T> std::unique_ptr<T> make_unique() {
        return std::unique_ptr<T>(new T);
    }

namespace Lexer
{

enum TokenType {first, second};

}

namespace Parser
{

struct  ExpressionAST {};
struct AccessExpression
    : public ExpressionAST
{
    std::unique_ptr<ExpressionAST> lhs;
    std::unique_ptr<ExpressionAST> rhs;
};

}

};

struct
{
    Wide::Lexer::TokenType type;
}* begin;

template<typename First, Wide::Lexer::TokenType first, typename F> std::unique_ptr<Wide::Parser::ExpressionAST>
RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseSingleToken;
    RecursiveParseSingleToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseSingleToken();
        }
    };
    RecursiveParseSingleToken();
    return std::move(cur_expr);
}

template<typename First, typename Second, Wide::Lexer::TokenType first, Wide::Lexer::TokenType second, typename F>
std::unique_ptr<Wide::Parser::ExpressionAST>
RecursiveParseLeftAssociativeBinaryExpression(F next) {
    auto cur_expr = next();
    std::function<void()> RecursiveParseDualToken;
    RecursiveParseDualToken = [&] {
        switch(begin->type) {
        case first:
            RecursiveParseLeftAssociativeBinaryExpression<First>(cur_expr, next);
            return RecursiveParseDualToken();
        case second:
            RecursiveParseLeftAssociativeBinaryExpression<Second>(cur_expr, next);
            return RecursiveParseDualToken();
        }
    };
    RecursiveParseDualToken();

    return std::move(cur_expr);
}

void CheckedIncrement();

template<typename T, typename F>
void RecursiveParseLeftAssociativeBinaryExpression(std::unique_ptr<Wide::Parser::ExpressionAST>& cur_expr, F f) {
    CheckedIncrement();
    auto new_expr = Wide::make_unique<T>();
    new_expr->lhs = std::move(cur_expr);
    new_expr->rhs = f();
    cur_expr = std::move(new_expr);
}

struct A
{
    std::unique_ptr<Wide::Parser::AccessExpression> RecursiveParseExpression();
};

int main()
{
    std::unique_ptr<Wide::Parser::ExpressionAST> current_expression;
    A* this_ptr;
    RecursiveParseLeftAssociativeBinaryExpression<Wide::Parser::AccessExpression>(current_expression,
        [&] { return this_ptr->RecursiveParseExpression(); });
}
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    That's definitely not the issue, since `Wide` is a namespace, and not a dependent name of any kind. Plus, just to be certain, I tried it and there was no change. – Puppy Dec 09 '11 at 16:09
  • @Howard: `Wide` is not a template class. It is a namespace. So definitely `Wide::template make_unique` is NOT correct, as the keyword `template` is not required here; in fact, it will cause error. – Nawaz Dec 09 '11 at 16:11
  • I would have agreed and voted to close this as a dupe of the [FAQ entry](http://stackoverflow.com/questions/610245/) on the matter, but I think that DeadMG knows about that stuff fairly well. He might still have forgotten some `typename` or `template`, but it's not that he doesn't know about them. – sbi Dec 09 '11 at 16:11
  • 3
    Fair enough. I could not find a definition for `Wide`. Oh, until the question update showing the definition for `make_unique`. These "dynamic questions" are challenging. ;-) – Howard Hinnant Dec 09 '11 at 16:14
  • @HowardHinnant: heh I knew about the Wide namespace from a previous question by the same OP (one, incidentally that pinpointed a marvellous implementation bug in MSVC++ 2010 lanbda compilation) – sehe Dec 09 '11 at 16:28
  • @sehe: Unfortunately, my question history is a *littany* of MSVC bugs. – Puppy Dec 09 '11 at 16:32
  • Definitely an upboat. That's an awful lot of work to go into for this one question. I'm gonna go get some food, but when I get back, I'll try for a more complete repro. – Puppy Dec 09 '11 at 17:05
  • @HowardHinnant: I posted a reproducing case in the OP. Your code was pretty close. – Puppy Dec 09 '11 at 18:22